
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
<title>Tagify - Tags input Component</title>
<meta name="description" content="Converts HTML input/textarea into Tags component">
<meta name="author" content="Yair Even-Or">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="keywords" content="tag, tags, tagging, tagify, javascript, component, web">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🔖</text></svg>">
<link rel="stylesheet" href="dist/tagify.css">
<link rel="stylesheet" href="https://unpkg.com/@yaireo/dragsort/dist/dragsort.css" media="print" onload="this.media='all'">
<style>
  html, body{ min-height:100%; scroll-behavior: smooth; }
  body{
    font:14px Arial;
    margin:0;
    overflow-x:hidden;
  } /* display:flex; flex-flow: column nowrap; align-items:center; justify-content:center; */

  .forkLink{ position:fixed; z-index:100; border:0; top:0; width:70px; right:0; opacity:.8; transition:.15s ease-out; }
  .forkLink:hover{ opacity:1; }
  .forkLink img{ width:100%; }

  section{ display:flex; }
  aside{ padding:4rem; box-sizing:border-box; width:50%; }
  section > .leftSide{
    max-width: 700px;
    border-right: 1px solid #CDE6FF;
    background:#F5FAFF;
    font-size: 1.1em;
  }
  section > .rightSide{  }

  .leftSide style {
    display: block;
    background: #333;
    color: white;
    font: 13px/1.8 Monaco, monospace;
    padding: 1.1em;
    margin-right: -2rem;
    white-space: pre;
    position:relative;
    border-radius: 5px;
  }

  style[contenteditable]::before{
    content: "Editable";
    position: absolute;
    top: 2px;
    right: 8px;
    color: salmon;
  }

  .leftSide{ padding-top:0; }
  .leftSide > header{ padding-top:4rem; position:relative; }
  .leftSide .demoLink{ float:right; width:38px; transform:translateX(calc(4rem + 50%)) translateY(-1px); position:relative; z-index:5; }
  .leftSide .demoLink svg{ box-shadow: 0 0 0 3px black inset; border-radius:50%; transition:.12s ease-out; }
  .leftSide .demoLink:hover svg{ box-shadow: 0 0 0 0 black inset; transform:scale(1.2); }
  .leftSide > header > h3{ margin-top:.5rem; }
  .leftSide h2{ color:salmon; text-transform:capitalize; font-size:1.6em; font-size:calc(1.3em + .5vw); line-height:1.2; margin-top:0; margin-bottom:0; }
  .leftSide h2 > a{
     color:inherit;
     text-decoration: none;
  }
  .leftSide h2 > a::before{
      content: '#';
      position:absolute; color:inherit; opacity:0; font-weight:normal;
      transform: translatex(-75%);
      transition: .1s ease-out;
  }
  .leftSide h2 > a:hover::before{ opacity:.6; transform:translateX(-150%); }

  .rightSide > .demoLink{
    position: absolute;
    left: 0;
    width: 38px;
    transform: translateX(-50%);
  }

  .rightSide .demoLink svg{ box-shadow: 0 0 0 3px black inset; border-radius:50%; transition:.12s ease-out; }
  .rightSide .demoLink:hover svg{ box-shadow: 0 0 0 0 black inset; transform:scale(1.2); }

  h3{ color:#388ffe; font-size:1.2em; margin:2rem 0 .5em; text-transform:capitalize; }
  h3:first-child{ margin-top:4.3rem; }
  xmp, pre{ white-space:normal; padding:8px; background:white; color:navy; border-radius:8px; }
  pre{ white-space:pre; overflow:auto; border:1px solid #eef5fa; box-shadow:0 8px 15px #388ffe25; }


  p{ line-height:1.4; }
  code{ padding:2px 5px; background:lightyellow; border-radius:8px; }
  pre code{ background:none; padding:0; }

  body :not(pre)>code[class*=language-], body pre[class*=language-]{ background:white; font-size:12px; margin-right:-2rem; }

  p{ line-height:1.4; }
  code{ padding:2px 3px; background:lightyellow; }
  pre code{ background:none; padding:0; }
  pre[class*=language-]{ max-height:1000px; }

  /* details expander */
  summary{ outline: none; font-size:1.3em; color: #333; cursor:pointer; margin:1em 0 0 0; user-select:none; transition: .15s ease-out; }
  details[open] summary{ margin-bottom: 1em; }

  .forkLink{ display:none; position:absolute; top:.7em; right:50%; transform:translateX(50%); }
  body > header{
      position: sticky;
      z-index: 100;
      top: -1px;
      left: 0;
      right: 0;
      display: flex;
      align-items: center;
      background:white;
      border-bottom: 1px solid #DDD;
      box-shadow: 0 0 6px rgba(0,0,0,.1);
      padding: .8em 1em .8em 3em;
  }

  header.isSticky .repoLink{
    height: 50px;
    margin: -10px 0 -20px 0;
    transition: .55s cubic-bezier(.5,0,.5,1.075);
  }

  header.isSticky .repoLink svg{ filter: none; }

 .repoLink{
    height: 70px;
    margin: -10px 0 -30px 0;
    transform: translateX(calc(50vw - 50%));
    transition: .2s ease-out;
 }
 .repoLink svg{
    filter: drop-shadow(0px 4px 0px white)
            drop-shadow(-4px 0px 0px white)
            drop-shadow(4px 0px 0px white)
            drop-shadow(0px 3px 1px rgba(0,0,0,.15));
  }
  body > header .liveLink{ color:#333; text-decoration:none; font-size:1.4em; position:relative; line-height:1; }
  body > header .liveLink::before{ content:'✎'; text-decoration:none; transform:translateX(-150%); position:absolute; top:1px; left:0; }
  body > header .liveLink:hover{ text-decoration: underline; }

  ul > li{
    margin: .8em 0;
  }
</style>

<script>
    // if IE, add IE tagify's polyfills
    !function( d ) {
        if( !d.currentScript ){
            var s = d.createElement( 'script' );
            s.src = 'dist/tagify.polyfills.min.js';
            d.head.appendChild( s );
        }
    }(document)
</script>

<script src="dist/tagify.js"></script>
<script src="https://unpkg.com/@yaireo/dragsort"></script>

<style>
    section > .rightSide{
        position: sticky;
        top: 0;
        align-self: start;
    }

    .tagify{
        min-width:400px;
        max-width:600px;
        margin: 0 0 1em 0;
    }

    label{ cursor:pointer; }
    .disabled tags{
        max-width:none;
        min-width:0;
        border:0;
        display: none;
    }

    .disabled tags tag,
    .disabled tags div{ display:none !important; }

    .showOriginal tags + input,
    .showOriginal tags + textarea{
        position: initial !important;
        left: 0 !important;
        transform: none !important;
        width:100%; padding:6px; border:2px solid #999;
        margin-bottom: 1em;
        border-radius: 5px;
        background: #F1F1F1;
    }

    /* .tagify.readonlyMix > tag:not([readonly]) div::before{ background:#d3e2e2; } */

    .tagify__input .borderd-blue > div::before{ border:2px solid #8DAFFA; }

    header label[for="toggle__tagifyStyleSettings"]{
        margin-left: auto;
        font-size: 1.5em;
        line-height: 1;
        position: relative;
        animation: 1s settingsIconFrames 3;
    }

    header label[for="toggle__tagifyStyleSettings"]::before{
      content: '';
      position: absolute;
      z-index: -1;
      top: -2px;
      left: 0;
      width: 100%;
      height: 120%;
      border-radius: 50%;
      pointer-events: none;
      animation: 1s settingsIconShockwavesFrames 3;
    }

    @keyframes settingsIconFrames {
      30%{ transform: scale(1.2);  }
    }

    @keyframes settingsIconShockwavesFrames {
      0%{ transform: scale(.6); box-shadow: 0 0 10px 2px red; }
      90%{ transform: scale(10); box-shadow: 0 0 40px 2px yellow; }
      100%{ transform: scale(12); opacity:0; }
    }
    /*
    header label[for="toggle__tagifyStyleSettings"]::before{
      content: "☝";
      position: absolute;
      font-size: 3rem;
      left: -15px;
      top: .5em;
      pointer-events: none;
    }
    */
    #toggle__tagifyStyleSettings:checked + .tagifyStyleSettings{
        transform: none;
    }

    .tagifyStyleSettings > label[for="toggle__tagifyStyleSettings"]{
        float: right;
    }

    .tagifyStyleSettings{
        position: fixed;
        z-index: 999;
        top: 0;
        right: 0;
        background: black;
        padding: 1em;
        color: white;
        border-bottom-left-radius: 6px;
        transform: translatex(100.5%);
        transition: .45s cubic-bezier(.65,0,.35,1);
    }

    .tagifyStyleSettings > div{
        display: flex;
        align-items: center;
        margin: 5px 0;
    }

    .tagifyStyleSettings > div > span{
        flex: 1;
        margin-right:1em;
    }

    @media only screen and (max-device-width: 600px){
        body{
            font-size: 12px;
        }

        body > header {
            position: relative;
            top: 0;
            margin-bottom: 3em;
        }

        section{
            flex-flow: column-reverse;
        }

        section > .leftSide{
            padding: 1.5rem;
            width: auto;
        }

        .leftSide > header{
            padding-top: 0;
        }

        section > .rightSide{
            width: 100%;
            padding: 1.5em;
            background: white;
            border-bottom: 1px solid #CDE6FF;
        }

        .tagify{
            min-width: auto;
            width: 100%;
        }

        .rightSide > .demoLink {
            display: none;
        }

    }
</style>
    </head>

    <body>
        <header>
    <a class='repoLink' title='Go To Repo' href='https://github.com/yairEO/tagify'>
        <svg height="100%" viewBox="0 0 280 150" fill-rule="evenodd" fill="#444" xmlns="http://www.w3.org/2000/svg">
            <path d="m86.601,17.001q3.1,0.6 4.65,2.1a5.288,5.288 0 0 1 1.073,1.435a4.56,4.56 0 0 1 0.477,2.065a9.273,9.273 0 0 1 -0.161,1.79q-0.198,1.007 -0.641,1.776a4.402,4.402 0 0 1 -1.048,1.234a4.933,4.933 0 0 1 -1.566,0.832q-1.69,0.563 -4.284,0.368q-6.891,-0.536 -11.466,-0.808a261.493,261.493 0 0 0 -2.584,-0.142q-4.517,-0.223 -11.555,-0.327a671.4,671.4 0 0 0 -1.695,-0.023q-3.7,18.4 -6.8,37q-1.1,6.8 -2.3,15.75q-1.073,8.006 -1.507,13.252a122.559,122.559 0 0 0 -0.093,1.198a6.511,6.511 0 0 1 -0.477,2.097a5.419,5.419 0 0 1 -1.923,2.353a8.433,8.433 0 0 1 -3.705,1.439a11.101,11.101 0 0 1 -1.595,0.111q-3.3,0 -5.1,-1.6a5.269,5.269 0 0 1 -1.789,-3.8a7.187,7.187 0 0 1 -0.011,-0.4q0,-1.809 0.426,-5.409a156.38,156.38 0 0 1 0.324,-2.541q0.75,-5.55 1.75,-11.65a316.042,316.042 0 0 0 0.755,-4.35q0.285,-1.719 0.521,-3.281a171.143,171.143 0 0 0 0.424,-2.969q1.1,-7.6 2.5,-15.1q1.4,-7.5 2.8,-14.2q0.3,-1.5 0.7,-3.45q0.4,-1.95 0.9,-4.35a138.602,138.602 0 0 0 -5.715,0.285q-5.22,0.377 -8.851,1.132a36.936,36.936 0 0 0 -0.834,0.183a30.163,30.163 0 0 0 -2.893,0.815q-2.915,0.992 -4.572,2.391a7.794,7.794 0 0 0 -0.485,0.444a7.93,7.93 0 0 0 -2.298,4.982a10.773,10.773 0 0 0 -0.052,1.068a11.586,11.586 0 0 0 1.531,5.721a14.176,14.176 0 0 0 0.469,0.779q0.4,0.7 0.4,1.5a3.407,3.407 0 0 1 -0.596,1.89q-0.347,0.53 -0.896,1.038a8.6,8.6 0 0 1 -0.758,0.622a9.129,9.129 0 0 1 -1.997,1.128a7.073,7.073 0 0 1 -2.653,0.522a4.734,4.734 0 0 1 -1.316,-0.174a3.7,3.7 0 0 1 -1.484,-0.826q-1.812,-1.54 -3.05,-4.272a18.514,18.514 0 0 1 -0.25,-0.578a16.991,16.991 0 0 1 -1.117,-4.269a21.937,21.937 0 0 1 -0.183,-2.881a21.081,21.081 0 0 1 0.881,-6.241a16.64,16.64 0 0 1 4.669,-7.409q5.55,-5.15 16.9,-7.55q11.35,-2.4 29.15,-2.4q9.389,0 15.536,0.292a148.328,148.328 0 0 1 2.014,0.108a121.637,121.637 0 0 1 5.415,0.446q2.459,0.261 4.624,0.611a67.021,67.021 0 0 1 1.411,0.243z"/>
            <path d="m72.595,99.73a13.778,13.778 0 0 0 4.706,0.771q7.9,0 13.1,-8.4a16.672,16.672 0 0 0 1.021,2.303a12.716,12.716 0 0 0 3.129,3.847a11.7,11.7 0 0 0 0.433,0.338a9.221,9.221 0 0 0 5.617,1.912a10.999,10.999 0 0 0 2.045,-0.194q1.837,-0.347 3.728,-1.313a20.867,20.867 0 0 0 3.127,-1.993a44.987,44.987 0 0 0 3.38,-2.85a54.085,54.085 0 0 0 5.32,-5.75a6.749,6.749 0 0 0 0.903,-1.349q0.997,-1.953 0.997,-4.851q0,-1.23 -0.257,-2.152a4.376,4.376 0 0 0 -0.493,-1.148a3.165,3.165 0 0 0 -0.301,-0.408a2.225,2.225 0 0 0 -1.749,-0.792a3.137,3.137 0 0 0 -1.86,0.618a4.764,4.764 0 0 0 -0.84,0.782a181.098,181.098 0 0 1 -1.496,1.767q-3.559,4.146 -5.804,6.183q-2.7,2.45 -4.6,2.45a3.041,3.041 0 0 1 -0.221,-0.008a2.394,2.394 0 0 1 -1.729,-0.892a2.866,2.866 0 0 1 -0.202,-0.274q-0.318,-0.489 -0.451,-1.125a4.872,4.872 0 0 1 -0.097,-1.001a19.067,19.067 0 0 1 0.003,-0.32q0.057,-3.429 1.318,-10.52a245.902,245.902 0 0 1 0.979,-5.16a306.979,306.979 0 0 0 0.187,-0.991q1.313,-7.044 1.313,-9.009a4.851,4.851 0 0 0 -0.002,-0.141q-0.098,-3.359 -4.898,-3.359q-2.3,0 -4.4,0.4a16.337,16.337 0 0 0 -0.167,-0.672q-0.453,-1.664 -1.083,-2.378a1.767,1.767 0 0 0 -0.381,-0.321q-0.833,-0.529 -2.569,-0.529a21.082,21.082 0 0 0 -6.202,0.924a24.014,24.014 0 0 0 -6.848,3.426q-6.15,4.35 -9.85,11.5q-3.7,7.15 -3.7,15.15a28.402,28.402 0 0 0 0.002,0.359q0.089,7.052 3.698,11.441a11.86,11.86 0 0 0 5.194,3.729zm15.406,-18.829l3.7,-18.4a11.707,11.707 0 0 0 -6.51,2.189a15.265,15.265 0 0 0 -1.24,0.961a19.709,19.709 0 0 0 -4.116,4.979a25.263,25.263 0 0 0 -1.534,3.071q-2.1,5 -2.1,10.6a12.33,12.33 0 0 0 0.099,1.617q0.25,1.888 1.135,2.964a3.841,3.841 0 0 0 0.016,0.019a4.086,4.086 0 0 0 2.786,1.464a5.852,5.852 0 0 0 0.664,0.036q2.2,0 4.2,-2.6q2,-2.6 2.9,-6.9z"/>
            <path d="m150.501,96.801l-6.4,4.3q-2.484,13.529 -5.971,23.278a75.028,75.028 0 0 1 -3.979,9.272a32.613,32.613 0 0 1 -3.394,5.323q-2.196,2.754 -4.72,4.346a13.857,13.857 0 0 1 -7.536,2.181a13.461,13.461 0 0 1 -3.83,-0.52a10.533,10.533 0 0 1 -4.47,-2.63a10.575,10.575 0 0 1 -3.087,-6.406a14.662,14.662 0 0 1 -0.113,-1.844a21.012,21.012 0 0 1 1.22,-6.857q0.948,-2.751 2.631,-5.64a43.813,43.813 0 0 1 1.749,-2.753a46.804,46.804 0 0 1 3.925,-4.881q6.006,-6.616 17.075,-15.169l0.6,-3.9q-1.7,2.7 -4.45,4.15q-2.75,1.45 -5.45,1.45a13.778,13.778 0 0 1 -4.706,-0.771a11.86,11.86 0 0 1 -5.194,-3.729q-3.609,-4.389 -3.698,-11.441a28.402,28.402 0 0 1 -0.002,-0.359q0,-8 3.7,-15.15q3.7,-7.15 9.85,-11.5a24.014,24.014 0 0 1 6.848,-3.426a21.082,21.082 0 0 1 6.202,-0.924q1.736,0 2.569,0.529a1.767,1.767 0 0 1 0.381,0.321q0.63,0.714 1.083,2.378a16.337,16.337 0 0 1 0.167,0.672q1.742,-0.367 3.989,-0.397a30.328,30.328 0 0 1 0.411,-0.003q2.5,0 3.55,0.75q1.001,0.715 1.048,2.748a8.802,8.802 0 0 1 0.002,0.202q0,1.113 -0.086,1.796a6.337,6.337 0 0 1 -0.014,0.104q-0.4,3.1 -2.5,15.7q-0.4,2.4 -0.85,5.25q-0.45,2.85 -0.95,6.05q7.7,-6 14.8,-10.6a7.207,7.207 0 0 1 0.783,-0.422q0.83,-0.378 1.517,-0.378a2.186,2.186 0 0 1 1.736,0.81a3.273,3.273 0 0 1 0.314,0.44q0.75,1.25 0.75,3.15q0,1.497 -0.297,2.571a4.943,4.943 0 0 1 -0.403,1.029q-0.7,1.3 -2.2,2.3q-8.6,5.8 -12.6,8.6zm-15.2,-17.2l3.2,-17.1a11.292,11.292 0 0 0 -6.139,2.023a14.986,14.986 0 0 0 -1.461,1.127a19.815,19.815 0 0 0 -3.986,4.835a25.642,25.642 0 0 0 -1.614,3.215q-2.1,5 -2.1,10.6a12.33,12.33 0 0 0 0.099,1.617q0.25,1.888 1.135,2.964a3.841,3.841 0 0 0 0.016,0.019a4.086,4.086 0 0 0 2.786,1.464a5.852,5.852 0 0 0 0.664,0.036a4.113,4.113 0 0 0 2.179,-0.655q0.657,-0.405 1.297,-1.058a10.912,10.912 0 0 0 0.924,-1.087q1.54,-2.053 2.435,-5.398a29.644,29.644 0 0 0 0.565,-2.602zm-15.7,56.2a2.805,2.805 0 0 0 1.542,-0.529q1.712,-1.132 3.688,-4.691a38.125,38.125 0 0 0 0.42,-0.78q3.15,-6 6.05,-19.1q-7.615,6.527 -11.024,11.783a27.437,27.437 0 0 0 -0.076,0.117q-2.151,3.353 -2.941,6.186a11.603,11.603 0 0 0 -0.459,3.114q0,1.7 0.65,2.8q0.65,1.1 2.15,1.1z" />
            <path d="m168.485,100.052a14.405,14.405 0 0 0 3.716,0.449a13.017,13.017 0 0 0 2.983,-0.354q5.115,-1.201 10.667,-6.47a57.215,57.215 0 0 0 4.85,-5.276a6.749,6.749 0 0 0 0.903,-1.349q0.997,-1.953 0.997,-4.851q0,-1.23 -0.257,-2.152a4.376,4.376 0 0 0 -0.493,-1.148a3.165,3.165 0 0 0 -0.301,-0.408a2.225,2.225 0 0 0 -1.749,-0.792a3.137,3.137 0 0 0 -1.86,0.618a4.764,4.764 0 0 0 -0.84,0.782a184.196,184.196 0 0 1 -1.465,1.737q-2.309,2.703 -4.074,4.528a42.627,42.627 0 0 1 -1.661,1.635q-2.7,2.5 -4.5,2.5a3.986,3.986 0 0 1 -0.537,-0.035q-0.802,-0.109 -1.37,-0.559a2.931,2.931 0 0 1 -0.793,-1.006q-0.8,-1.6 -0.8,-4.6a18.033,18.033 0 0 1 0.045,-1.205q0.213,-3.139 1.42,-9.222a243.091,243.091 0 0 1 0.935,-4.473a333.394,333.394 0 0 0 0.054,-0.257q1.444,-6.943 1.758,-9.964a13.831,13.831 0 0 0 0.088,-1.379q0,-2.736 -3.302,-3.27a11.317,11.317 0 0 0 -1.798,-0.13q-2.114,0 -3.489,0.493a4.516,4.516 0 0 0 -1.311,0.707a4.047,4.047 0 0 0 -0.275,0.242q-0.903,0.867 -1.571,2.516a15.701,15.701 0 0 0 -0.554,1.642a96.723,96.723 0 0 0 -1.259,4.709a115.141,115.141 0 0 0 -1.691,8.441q-0.755,4.627 -1.014,8.155a46.575,46.575 0 0 0 -0.136,3.395a35.4,35.4 0 0 0 0.086,2.505q0.417,5.88 2.864,9.695a9.47,9.47 0 0 0 5.734,4.151zm2.207,-53.423a14.478,14.478 0 0 0 2.309,0.172a12.934,12.934 0 0 0 2.784,-0.284a8.852,8.852 0 0 0 4.116,-2.066a7.555,7.555 0 0 0 2.219,-3.529a9.921,9.921 0 0 0 0.381,-2.821a8.542,8.542 0 0 0 -0.056,-0.994a5.858,5.858 0 0 0 -2.244,-4.106a7.5,7.5 0 0 0 -0.488,-0.351q-1.281,-0.846 -2.946,-1.198a12.408,12.408 0 0 0 -2.566,-0.251a12.993,12.993 0 0 0 -1.617,0.098a9.725,9.725 0 0 0 -5.233,2.252a9.427,9.427 0 0 0 -0.413,0.374a7.313,7.313 0 0 0 -2.337,5.476a9.57,9.57 0 0 0 0.205,2.036a6.381,6.381 0 0 0 1.895,3.414q1.477,1.371 3.991,1.778z"/>
            <path d="m225.87,72.406a2.564,2.564 0 0 0 -1.469,-0.405q-0.9,0 -1.8,0.1q-0.9,0.1 -1.7,0.2q-4.9,0.7 -8.9,0.7a27.307,27.307 0 0 1 -0.332,-0.002q-0.995,-0.012 -1.768,-0.098a99.703,99.703 0 0 0 -1.269,-0.083q-2,-0.117 -3.531,-0.117a1.775,1.775 0 0 0 -0.4,0.047q-0.66,0.153 -1.419,0.797a8.2,8.2 0 0 0 -0.681,0.656a12.714,12.714 0 0 0 -0.601,0.693a14.05,14.05 0 0 0 -1.749,2.807a14.72,14.72 0 0 0 -0.07,0.15q-0.876,1.899 -0.78,3.15a231.116,231.116 0 0 1 1.245,5.963a193.785,193.785 0 0 1 1.605,9.437a98.157,98.157 0 0 1 0.065,0.457a106.247,106.247 0 0 1 0.985,14.643a56.187,56.187 0 0 1 -0.076,3.047q-0.252,4.62 -1.342,6.338a1.861,1.861 0 0 1 -1.582,1.015q-1.35,0 -2.059,-7.724a116.118,116.118 0 0 1 -0.241,-3.126a359.188,359.188 0 0 1 -0.011,-0.169a328.402,328.402 0 0 1 -0.689,-20.981q0,-7.4 1,-17.2a179.38,179.38 0 0 0 2.296,-2.304q25.804,-26.376 25.804,-48.896a33.422,33.422 0 0 0 -0.118,-2.866q-0.196,-2.281 -0.72,-4.204a15.529,15.529 0 0 0 -1.812,-4.23a11.636,11.636 0 0 0 -0.486,-0.711q-1.209,-1.635 -2.792,-2.503a8.323,8.323 0 0 0 -4.072,-0.986a11.515,11.515 0 0 0 -2.712,0.323q-5.148,1.243 -9.76,7.268a44.915,44.915 0 0 0 -3.478,5.359a80.564,80.564 0 0 0 -2.972,5.879q-2.701,5.91 -4.88,12.965a155.054,155.054 0 0 0 -3.548,13.856q-4.15,19.75 -4.15,38.95q0,20.6 3.05,30.8q1.18,3.945 2.98,6.364a8.709,8.709 0 0 0 7.27,3.836a9.618,9.618 0 0 0 6.798,-2.694a14.408,14.408 0 0 0 2.402,-3.006a15.999,15.999 0 0 0 0.125,-0.207q2.355,-3.968 3.067,-10.33a53.781,53.781 0 0 0 0.308,-5.963a72.868,72.868 0 0 0 -0.091,-3.501q-0.092,-1.893 -0.275,-4.071a173.83,173.83 0 0 0 -0.484,-4.878q-0.85,-7.55 -2.15,-13.75a78.893,78.893 0 0 0 1.457,0.278q3.447,0.622 5.443,0.622a36.857,36.857 0 0 0 0.881,-0.01q4.937,-0.118 7.757,-1.584a6.603,6.603 0 0 0 3.762,-6.306a9.695,9.695 0 0 0 -0.081,-1.309q-0.248,-1.809 -1.25,-2.486zm-11.569,-56.505q-1.8,0 -4.45,6.1q-2.65,6.1 -5.25,16.15a247.056,247.056 0 0 0 -3.506,15.722a282.802,282.802 0 0 0 -0.994,5.528a78.429,78.429 0 0 0 8.466,-11.518a65.753,65.753 0 0 0 3.834,-7.432a58.244,58.244 0 0 0 2.959,-8.379q1.277,-4.824 1.424,-9.313a35.708,35.708 0 0 0 0.017,-0.758a22.746,22.746 0 0 0 -0.074,-1.916q-0.356,-4.184 -2.426,-4.184z"/>
            <path d="m272.701,88.201l-19.2,12.9q-2.484,13.529 -5.971,23.278a75.028,75.028 0 0 1 -3.979,9.272a32.613,32.613 0 0 1 -3.394,5.323q-2.196,2.754 -4.72,4.346a13.857,13.857 0 0 1 -7.536,2.181a13.461,13.461 0 0 1 -3.83,-0.52a10.533,10.533 0 0 1 -4.47,-2.63a10.575,10.575 0 0 1 -3.087,-6.406a14.662,14.662 0 0 1 -0.113,-1.844q0,-4.7 2.15,-9.6a26.719,26.719 0 0 1 1.915,-3.54q2.246,-3.543 6.085,-7.71a84.129,84.129 0 0 1 4.595,-4.584q4.933,-4.585 11.955,-9.966l0.3,-2.3q0.536,-2.833 1.189,-7.541a380.176,380.176 0 0 0 0.411,-3.059q-1.446,5.207 -3.729,8.609a17.233,17.233 0 0 1 -1.871,2.341q-2.806,2.923 -5.856,3.568a8.413,8.413 0 0 1 -1.744,0.182a8.258,8.258 0 0 1 -6.413,-2.947a12.208,12.208 0 0 1 -0.937,-1.203a15.755,15.755 0 0 1 -2.398,-5.735a22.346,22.346 0 0 1 -0.452,-4.615a98.01,98.01 0 0 1 0.299,-7.829a75.931,75.931 0 0 1 0.701,-5.921a78.663,78.663 0 0 1 1.646,-7.686a98.87,98.87 0 0 1 1.654,-5.564q0.706,-2.118 1.811,-3.389a5.856,5.856 0 0 1 0.989,-0.911a5.57,5.57 0 0 1 1.486,-0.74q1.655,-0.56 4.214,-0.56q1.743,0 2.639,0.44a2.096,2.096 0 0 1 0.411,0.26q0.827,0.681 0.849,2.024a4.518,4.518 0 0 1 0.001,0.076a3.471,3.471 0 0 1 -0.039,0.434q-0.137,1.008 -0.764,3.706a198.933,198.933 0 0 1 -0.297,1.26a124.01,124.01 0 0 0 -0.875,3.42q-0.39,1.625 -0.686,3.086a74.279,74.279 0 0 0 -0.039,0.194q-0.8,4.1 -1.4,7.85a63.407,63.407 0 0 0 -0.316,2.212q-0.284,2.288 -0.284,3.938a10.271,10.271 0 0 0 0.074,1.292q0.32,2.508 2.026,2.508q1.452,0 3.606,-2.81a22.589,22.589 0 0 0 0.144,-0.19a23.883,23.883 0 0 0 1.436,-2.178q1.384,-2.344 2.874,-5.771a89.066,89.066 0 0 0 0.49,-1.151q1.952,-4.67 3.817,-10.982a175.362,175.362 0 0 0 1.133,-4.018a12.336,12.336 0 0 1 0.583,-1.709q0.378,-0.875 0.862,-1.536a5.37,5.37 0 0 1 1.005,-1.055a5.254,5.254 0 0 1 1.595,-0.835q1.383,-0.465 3.355,-0.465q1.787,0 2.729,0.362a2.402,2.402 0 0 1 0.471,0.238a1.919,1.919 0 0 1 0.794,1.132q0.093,0.339 0.104,0.749a4.199,4.199 0 0 1 0.002,0.119q0,2.5 -2.6,17.2l-2.6,16.1q7.7,-5.8 15,-10.6a7.207,7.207 0 0 1 0.783,-0.422q0.83,-0.378 1.517,-0.378a2.186,2.186 0 0 1 1.736,0.81a3.273,3.273 0 0 1 0.314,0.44q0.75,1.25 0.75,3.15q0,1.497 -0.297,2.571a4.943,4.943 0 0 1 -0.403,1.029q-0.7,1.3 -2.2,2.3zm-38.506,42.54a36.133,36.133 0 0 0 0.406,-0.74q2.4,-4.49 4.8,-13.595a184.128,184.128 0 0 0 1.4,-5.705a90.074,90.074 0 0 0 -4.397,3.95q-4.381,4.222 -6.753,7.9q-2.176,3.374 -2.979,6.211a11.534,11.534 0 0 0 -0.471,3.139q0,1.7 0.65,2.8q0.65,1.1 2.15,1.1q2.335,0 5.194,-5.06z"/>
        </svg>
    </a>
    <a class='forkLink' title='Go To Repo' href='https://github.com/yairEO/tagify'>
        <img src='https://github.githubassets.com/images/modules/logos_page/GitHub-Logo.png' alt='GitHub Repo'>
    </a>
</header>

        <!-- SVG codeopen logo template -->
        <svg style='display:none' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
            <g id='codepen-logo' viewBox="0 0 120 120">
                <path class="inner-box" d="M97 48v-1a3 3 0 00-1-1L62 23h-4L24 46a3 3 0 00-1 1v26a4 4 0 001 0v1l34 23a3 3 0 004 0l34-23a3 3 0 001-1v-1-23-1zM63 32l25 17-11 7-14-9V32zm-6 0v15l-14 9-11-7 25-17zM29 55l8 5-8 5V55zm28 33L32 71l11-7 14 9v15zm3-20l-11-8 11-8 11 8-11 8zm3 20V73l14-9 11 7-25 17zm28-23l-8-5 8-5v10z"/>
            </g>
        </svg>
        <!-- template end -->

        <form>
            <!----------- SECTION -------------->
            


<section id='section-basic'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-basic'>basic</a></h2>
        </header>
        

        
                <p>
                    Passing the input element as a parameter to <em>Tagify</em> will transform it into a tags-component.
                    Without any settings, the user will be allowed to create any tags they want, without a count limit.
                </p>
                <p>
                    If the input element has a pre-defined <code>value</code> attribute, tags will be created from it.
                </p>
                <p>
                    (<strong>Try <em>Double-clicking</em> a tag to edit it</strong>)
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input name=&#39;basic&#39; value=&#39;tag1, tag2&#39; autofocus&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>// The DOM element you wish to replace with Tagify
var input = document.querySelector('input[name=basic]');

// initialize Tagify on the above input node reference
new Tagify(input)</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/PoNqYVY'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input name='basic' value='tag1, tag2' autofocus>
    </aside>

    <script>
        (function(){
        // The DOM element you wish to replace with Tagify
var input = document.querySelector('input[name=basic]');

// initialize Tagify on the above input node reference
new Tagify(input)
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-input'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-input'>Text Input</a></h2>
        </header>
        

        
                <h3>In this example:</h3>
                <ul>
                    <li>Tagify instance with initial <em>settings</em> Object</li>
                    <li>Only allow tags from the whitelist to exist</li>
                    <li>Manually create initial <em>whitelist</em> from the original input's value</li>
                    <li>Simulate fetching <em>whitelist</em> (from a server) as the user is typing</li>
                    <li><em>"Remove all tags"</em> by an external button</li>
                    <li>Listen to various Tagify <a href='https://github.com/yairEO/tagify#events'>events</a></li>
                    <li>Un-listen to specific event (see <code>onAddTag</code> function)</li>
                </ul>
                <h3>Intro:</h3>
                <p>
                    The input element is pre-ocupied with <strong>4</strong> tags. The <em>last</em> tag, <em>"CSS"</em>,
                    has the same value as the <em>first</em> tag, and will be <em>removed</em>, because the <code>duplicates</code>
                    setting is set to <code>true</code>.
                </p>
                <p>
                    Note that <code>whitelist</code> & <code>blacklist</code> may also be set on to <i>Tagify</i> as <code>data-</code>
                    attributes on the input tag itself as a list of tags seperated by the same delimeter defined in the Tagify's configuration.
                    Default is comma (<code>,</code>)
                </p>
                <p>
                    As text is typed, the <code>onInput</code> Tagify event is fired, on each key stroke.
                    Reseting the current whitelist and fetching a new one (from <code>mockAjax()</code>)
                </p>
                <p>
                    In real-life scenarios, a developer might wish to use different <em>whitelists</em> suggested to the user,
                    according to the typed text, asusming the possibilities are too large to load at-once as the inital <em>whitelist</em>.
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;!-- https://codepen.io/vsync/pen/PoNqYVY --&gt;
&lt;input name=&#39;input&#39; class=&#39;some_class_name&#39; placeholder=&#39;Type an English letter&#39; value=&#39;css, html, javascript, css&#39; data-blacklist=&#39;.NET, PHP&#39;&gt;
&lt;br/&gt;
&lt;button class=&#39;tags--removeAllBtn&#39; type=&#39;button&#39;&gt;Remove all these tags ⬆&lt;/button&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var inputElm = document.querySelector('input[name=input]'),
    whitelist = ["A# .NET", "A# (Axiom)", "A-0 System", "A+", "A++", "ABAP", "ABC", "ABC ALGOL", "ABSET", "ABSYS", "ACC", "Accent", "Ace DASL", "ACL2", "Avicsoft", "ACT-III", "Action!", "ActionScript", "Ada", "Adenine", "Agda", "Agilent VEE", "Agora", "AIMMS", "Alef", "ALF", "ALGOL 58", "ALGOL 60", "ALGOL 68", "ALGOL W", "Alice", "Alma-0", "AmbientTalk", "Amiga E", "AMOS", "AMPL", "Apex (Salesforce.com)", "APL", "AppleScript", "Arc", "ARexx", "Argus", "AspectJ", "Assembly language", "ATS", "Ateji PX", "AutoHotkey", "Autocoder", "AutoIt", "AutoLISP / Visual LISP", "Averest", "AWK", "Axum", "Active Server Pages", "ASP.NET", "B", "Babbage", "Bash", "BASIC", "bc", "BCPL", "BeanShell", "Batch (Windows/Dos)", "Bertrand", "BETA", "Bigwig", "Bistro", "BitC", "BLISS", "Blockly", "BlooP", "Blue", "Boo", "Boomerang", "Bourne shell (including bash and ksh)", "BREW", "BPEL", "B", "C--", "C++ – ISO/IEC 14882", "C# – ISO/IEC 23270", "C/AL", "Caché ObjectScript", "C Shell", "Caml", "Cayenne", "CDuce", "Cecil", "Cesil", "Céu", "Ceylon", "CFEngine", "CFML", "Cg", "Ch", "Chapel", "Charity", "Charm", "Chef", "CHILL", "CHIP-8", "chomski", "ChucK", "CICS", "Cilk", "Citrine (programming language)", "CL (IBM)", "Claire", "Clarion", "Clean", "Clipper", "CLIPS", "CLIST", "Clojure", "CLU", "CMS-2", "COBOL – ISO/IEC 1989", "CobolScript – COBOL Scripting language", "Cobra", "CODE", "CoffeeScript", "ColdFusion", "COMAL", "Combined Programming Language (CPL)", "COMIT", "Common Intermediate Language (CIL)", "Common Lisp (also known as CL)", "COMPASS", "Component Pascal", "Constraint Handling Rules (CHR)", "COMTRAN", "Converge", "Cool", "Coq", "Coral 66", "Corn", "CorVision", "COWSEL", "CPL", "CPL", "Cryptol", "csh", "Csound", "CSP", "CUDA", "Curl", "Curry", "Cybil", "Cyclone", "Cython", "Java", "Javascript", "M2001", "M4", "M#", "Machine code", "MAD (Michigan Algorithm Decoder)", "MAD/I", "Magik", "Magma", "make", "Maple", "MAPPER now part of BIS", "MARK-IV now VISION:BUILDER", "Mary", "MASM Microsoft Assembly x86", "MATH-MATIC", "Mathematica", "MATLAB", "Maxima (see also Macsyma)", "Max (Max Msp – Graphical Programming Environment)", "Maya (MEL)", "MDL", "Mercury", "Mesa", "Metafont", "Microcode", "MicroScript", "MIIS", "Milk (programming language)", "MIMIC", "Mirah", "Miranda", "MIVA Script", "ML", "Model 204", "Modelica", "Modula", "Modula-2", "Modula-3", "Mohol", "MOO", "Mortran", "Mouse", "MPD", "Mathcad", "MSIL – deprecated name for CIL", "MSL", "MUMPS", "Mystic Programming L"];


// initialize Tagify on the above input node reference
var tagify = new Tagify(inputElm, {
    enforceWhitelist: true,
    whitelist: inputElm.value.trim().split(/\s*,\s*/) // Array of values. stackoverflow.com/a/43375571/104380
})



// "remove all tags" button event listener
document.querySelector('.tags--removeAllBtn')
    .addEventListener('click', tagify.removeAllTags.bind(tagify))

// Chainable event listeners
tagify.on('add', onAddTag)
      .on('remove', onRemoveTag)
      .on('input', onInput)
      .on('edit', onTagEdit)
      .on('invalid', onInvalidTag)
      .on('click', onTagClick)
      .on('focus', onTagifyFocusBlur)
      .on('blur', onTagifyFocusBlur)
      .on('dropdown:hide dropdown:show', e => console.log(e.type))
      .on('dropdown:select', onDropdownSelect)

var mockAjax = (function mockAjax(){
    var timeout;
    return function(duration){
        clearTimeout(timeout); // abort last request
        return new Promise(function(resolve, reject){
            timeout = setTimeout(resolve, duration || 700, whitelist)
        })
    }
})()

// tag added callback
function onAddTag(e){
    console.log("onAddTag: ", e.detail);
    console.log("original input value: ", inputElm.value)
    tagify.off('add', onAddTag) // exmaple of removing a custom Tagify event
}

// tag remvoed callback
function onRemoveTag(e){
    console.log("onRemoveTag:", e.detail, "tagify instance value:", tagify.value)
}

// on character(s) added/removed (user is typing/deleting)
function onInput(e){
    console.log("onInput: ", e.detail);
    tagify.whitelist = null; // reset current whitelist
    tagify.loading(true) // show the loader animation

    // get new whitelist from a delayed mocked request (Promise)
    mockAjax()
        .then(function(result){
            tagify.settings.whitelist = result.concat(tagify.value) // add already-existing tags to the new whitelist array

            tagify
                .loading(false)
                // render the suggestions dropdown.
                .dropdown.show(e.detail.value);
        })
        .catch(err => tagify.dropdown.hide())
}

function onTagEdit(e){
    console.log("onTagEdit: ", e.detail);
}

// invalid tag added callback
function onInvalidTag(e){
    console.log("onInvalidTag: ", e.detail);
}

// invalid tag added callback
function onTagClick(e){
    console.log(e.detail);
    console.log("onTagClick: ", e.detail);
}

function onTagifyFocusBlur(e){
    console.log(e.type, "event fired")
}

function onDropdownSelect(e){
    console.log("onDropdownSelect: ", e.detail)
}</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/OJNVyQR'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <!-- https://codepen.io/vsync/pen/PoNqYVY -->
<input name='input' class='some_class_name' placeholder='Type an English letter' value='css, html, javascript, css' data-blacklist='.NET, PHP'>
<br/>
<button class='tags--removeAllBtn' type='button'>Remove all these tags ⬆</button>
    </aside>

    <script>
        (function(){
        var inputElm = document.querySelector('input[name=input]'),
    whitelist = ["A# .NET", "A# (Axiom)", "A-0 System", "A+", "A++", "ABAP", "ABC", "ABC ALGOL", "ABSET", "ABSYS", "ACC", "Accent", "Ace DASL", "ACL2", "Avicsoft", "ACT-III", "Action!", "ActionScript", "Ada", "Adenine", "Agda", "Agilent VEE", "Agora", "AIMMS", "Alef", "ALF", "ALGOL 58", "ALGOL 60", "ALGOL 68", "ALGOL W", "Alice", "Alma-0", "AmbientTalk", "Amiga E", "AMOS", "AMPL", "Apex (Salesforce.com)", "APL", "AppleScript", "Arc", "ARexx", "Argus", "AspectJ", "Assembly language", "ATS", "Ateji PX", "AutoHotkey", "Autocoder", "AutoIt", "AutoLISP / Visual LISP", "Averest", "AWK", "Axum", "Active Server Pages", "ASP.NET", "B", "Babbage", "Bash", "BASIC", "bc", "BCPL", "BeanShell", "Batch (Windows/Dos)", "Bertrand", "BETA", "Bigwig", "Bistro", "BitC", "BLISS", "Blockly", "BlooP", "Blue", "Boo", "Boomerang", "Bourne shell (including bash and ksh)", "BREW", "BPEL", "B", "C--", "C++ – ISO/IEC 14882", "C# – ISO/IEC 23270", "C/AL", "Caché ObjectScript", "C Shell", "Caml", "Cayenne", "CDuce", "Cecil", "Cesil", "Céu", "Ceylon", "CFEngine", "CFML", "Cg", "Ch", "Chapel", "Charity", "Charm", "Chef", "CHILL", "CHIP-8", "chomski", "ChucK", "CICS", "Cilk", "Citrine (programming language)", "CL (IBM)", "Claire", "Clarion", "Clean", "Clipper", "CLIPS", "CLIST", "Clojure", "CLU", "CMS-2", "COBOL – ISO/IEC 1989", "CobolScript – COBOL Scripting language", "Cobra", "CODE", "CoffeeScript", "ColdFusion", "COMAL", "Combined Programming Language (CPL)", "COMIT", "Common Intermediate Language (CIL)", "Common Lisp (also known as CL)", "COMPASS", "Component Pascal", "Constraint Handling Rules (CHR)", "COMTRAN", "Converge", "Cool", "Coq", "Coral 66", "Corn", "CorVision", "COWSEL", "CPL", "CPL", "Cryptol", "csh", "Csound", "CSP", "CUDA", "Curl", "Curry", "Cybil", "Cyclone", "Cython", "Java", "Javascript", "M2001", "M4", "M#", "Machine code", "MAD (Michigan Algorithm Decoder)", "MAD/I", "Magik", "Magma", "make", "Maple", "MAPPER now part of BIS", "MARK-IV now VISION:BUILDER", "Mary", "MASM Microsoft Assembly x86", "MATH-MATIC", "Mathematica", "MATLAB", "Maxima (see also Macsyma)", "Max (Max Msp – Graphical Programming Environment)", "Maya (MEL)", "MDL", "Mercury", "Mesa", "Metafont", "Microcode", "MicroScript", "MIIS", "Milk (programming language)", "MIMIC", "Mirah", "Miranda", "MIVA Script", "ML", "Model 204", "Modelica", "Modula", "Modula-2", "Modula-3", "Mohol", "MOO", "Mortran", "Mouse", "MPD", "Mathcad", "MSIL – deprecated name for CIL", "MSL", "MUMPS", "Mystic Programming L"];


// initialize Tagify on the above input node reference
var tagify = new Tagify(inputElm, {
    enforceWhitelist: true,
    whitelist: inputElm.value.trim().split(/\s*,\s*/) // Array of values. stackoverflow.com/a/43375571/104380
})



// "remove all tags" button event listener
document.querySelector('.tags--removeAllBtn')
    .addEventListener('click', tagify.removeAllTags.bind(tagify))

// Chainable event listeners
tagify.on('add', onAddTag)
      .on('remove', onRemoveTag)
      .on('input', onInput)
      .on('edit', onTagEdit)
      .on('invalid', onInvalidTag)
      .on('click', onTagClick)
      .on('focus', onTagifyFocusBlur)
      .on('blur', onTagifyFocusBlur)
      .on('dropdown:hide dropdown:show', e => console.log(e.type))
      .on('dropdown:select', onDropdownSelect)

var mockAjax = (function mockAjax(){
    var timeout;
    return function(duration){
        clearTimeout(timeout); // abort last request
        return new Promise(function(resolve, reject){
            timeout = setTimeout(resolve, duration || 700, whitelist)
        })
    }
})()

// tag added callback
function onAddTag(e){
    console.log("onAddTag: ", e.detail);
    console.log("original input value: ", inputElm.value)
    tagify.off('add', onAddTag) // exmaple of removing a custom Tagify event
}

// tag remvoed callback
function onRemoveTag(e){
    console.log("onRemoveTag:", e.detail, "tagify instance value:", tagify.value)
}

// on character(s) added/removed (user is typing/deleting)
function onInput(e){
    console.log("onInput: ", e.detail);
    tagify.whitelist = null; // reset current whitelist
    tagify.loading(true) // show the loader animation

    // get new whitelist from a delayed mocked request (Promise)
    mockAjax()
        .then(function(result){
            tagify.settings.whitelist = result.concat(tagify.value) // add already-existing tags to the new whitelist array

            tagify
                .loading(false)
                // render the suggestions dropdown.
                .dropdown.show(e.detail.value);
        })
        .catch(err => tagify.dropdown.hide())
}

function onTagEdit(e){
    console.log("onTagEdit: ", e.detail);
}

// invalid tag added callback
function onInvalidTag(e){
    console.log("onInvalidTag: ", e.detail);
}

// invalid tag added callback
function onTagClick(e){
    console.log(e.detail);
    console.log("onTagClick: ", e.detail);
}

function onTagifyFocusBlur(e){
    console.log(e.type, "event fired")
}

function onDropdownSelect(e){
    console.log("onDropdownSelect: ", e.detail)
}
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-input-suggestions-styled-as-tags'>
    <aside class='leftSide'>
        

        
                <header>
                    <h3>Same using custom suggestions:</h3>
                </header>
                <p>The dropdown will appear immediately when Tagify has focus.</p>
                <p>The suggestions are styled as tags this time. Clicking on a suggested it, it will be added to <em>Tagify</em></p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input name=&#39;input-custom-dropdown&#39; class=&#39;tagify--custom-dropdown&#39; placeholder=&#39;Type an English letter&#39; value=&#39;css, html, javascript&#39;&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input[name="input-custom-dropdown"]'),
    // init Tagify script on the above inputs
    tagify = new Tagify(input, {
        whitelist: ["A# .NET", "A# (Axiom)", "A-0 System", "A+", "A++", "ABAP", "ABC", "ABC ALGOL", "ABSET", "ABSYS", "ACC", "Accent", "Ace DASL", "ACL2", "Avicsoft", "ACT-III", "Action!", "ActionScript", "Ada", "Adenine", "Agda", "Agilent VEE", "Agora", "AIMMS", "Alef", "ALF", "ALGOL 58", "ALGOL 60", "ALGOL 68", "ALGOL W", "Alice", "Alma-0", "AmbientTalk", "Amiga E", "AMOS", "AMPL", "Apex (Salesforce.com)", "APL", "AppleScript", "Arc", "ARexx", "Argus", "AspectJ", "Assembly language", "ATS", "Ateji PX", "AutoHotkey", "Autocoder", "AutoIt", "AutoLISP / Visual LISP", "Averest", "AWK", "Axum", "Active Server Pages", "ASP.NET", "B", "Babbage", "Bash", "BASIC", "bc", "BCPL", "BeanShell", "Batch (Windows/Dos)", "Bertrand", "BETA", "Bigwig", "Bistro", "BitC", "BLISS", "Blockly", "BlooP", "Blue", "Boo", "Boomerang", "Bourne shell (including bash and ksh)", "BREW", "BPEL", "B", "C--", "C++ – ISO/IEC 14882", "C# – ISO/IEC 23270", "C/AL", "Caché ObjectScript", "C Shell", "Caml", "Cayenne", "CDuce", "Cecil", "Cesil", "Céu", "Ceylon", "CFEngine", "CFML", "Cg", "Ch", "Chapel", "Charity", "Charm", "Chef", "CHILL", "CHIP-8", "chomski", "ChucK", "CICS", "Cilk", "Citrine (programming language)", "CL (IBM)", "Claire", "Clarion", "Clean", "Clipper", "CLIPS", "CLIST", "Clojure", "CLU", "CMS-2", "COBOL – ISO/IEC 1989", "CobolScript – COBOL Scripting language", "Cobra", "CODE", "CoffeeScript", "ColdFusion", "COMAL", "Combined Programming Language (CPL)", "COMIT", "Common Intermediate Language (CIL)", "Common Lisp (also known as CL)", "COMPASS", "Component Pascal", "Constraint Handling Rules (CHR)", "COMTRAN", "Converge", "Cool", "Coq", "Coral 66", "Corn", "CorVision", "COWSEL", "CPL", "CPL", "Cryptol", "csh", "Csound", "CSP", "CUDA", "Curl", "Curry", "Cybil", "Cyclone", "Cython", "Java", "Javascript", "M2001", "M4", "M#", "Machine code", "MAD (Michigan Algorithm Decoder)", "MAD/I", "Magik", "Magma", "make", "Maple", "MAPPER now part of BIS", "MARK-IV now VISION:BUILDER", "Mary", "MASM Microsoft Assembly x86", "MATH-MATIC", "Mathematica", "MATLAB", "Maxima (see also Macsyma)", "Max (Max Msp – Graphical Programming Environment)", "Maya (MEL)", "MDL", "Mercury", "Mesa", "Metafont", "Microcode", "MicroScript", "MIIS", "Milk (programming language)", "MIMIC", "Mirah", "Miranda", "MIVA Script", "ML", "Model 204", "Modelica", "Modula", "Modula-2", "Modula-3", "Mohol", "MOO", "Mortran", "Mouse", "MPD", "Mathcad", "MSIL – deprecated name for CIL", "MSL", "MUMPS", "Mystic Programming L"],
        maxTags: 10,
        dropdown: {
            maxItems: 20,           // <- mixumum allowed rendered suggestions
            classname: 'tags-look', // <- custom classname for this dropdown, so it could be targeted
            enabled: 0,             // <- show suggestions on focus
            closeOnSelect: false    // <- do not hide the suggestions dropdown once an item has been selected
        }
    })</code></pre>
            
            <h3>CSS</h3>
            <style contenteditable>.tags-look .tagify__dropdown__item{
    display: inline-block;
    vertical-align: middle;
    border-radius: 3px;
    padding: .3em .5em;
    border: 1px solid #CCC;
    background: #F3F3F3;
    margin: .2em;
    font-size: .85em;
    color: black;
    transition: 0s;
}

.tags-look .tagify__dropdown__item--active{
    border-color: black;
}

.tags-look .tagify__dropdown__item:hover{
    background: lightyellow;
    border-color: gold;
}

.tags-look .tagify__dropdown__item--hidden {
    max-width: 0;
    max-height: initial;
    padding: .3em 0;
    margin: .2em 0;
    white-space: nowrap;
    text-indent: -20px;
    border: 0;
}</style>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/bGpdVMW'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input name='input-custom-dropdown' class='tagify--custom-dropdown' placeholder='Type an English letter' value='css, html, javascript'>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input[name="input-custom-dropdown"]'),
    // init Tagify script on the above inputs
    tagify = new Tagify(input, {
        whitelist: ["A# .NET", "A# (Axiom)", "A-0 System", "A+", "A++", "ABAP", "ABC", "ABC ALGOL", "ABSET", "ABSYS", "ACC", "Accent", "Ace DASL", "ACL2", "Avicsoft", "ACT-III", "Action!", "ActionScript", "Ada", "Adenine", "Agda", "Agilent VEE", "Agora", "AIMMS", "Alef", "ALF", "ALGOL 58", "ALGOL 60", "ALGOL 68", "ALGOL W", "Alice", "Alma-0", "AmbientTalk", "Amiga E", "AMOS", "AMPL", "Apex (Salesforce.com)", "APL", "AppleScript", "Arc", "ARexx", "Argus", "AspectJ", "Assembly language", "ATS", "Ateji PX", "AutoHotkey", "Autocoder", "AutoIt", "AutoLISP / Visual LISP", "Averest", "AWK", "Axum", "Active Server Pages", "ASP.NET", "B", "Babbage", "Bash", "BASIC", "bc", "BCPL", "BeanShell", "Batch (Windows/Dos)", "Bertrand", "BETA", "Bigwig", "Bistro", "BitC", "BLISS", "Blockly", "BlooP", "Blue", "Boo", "Boomerang", "Bourne shell (including bash and ksh)", "BREW", "BPEL", "B", "C--", "C++ – ISO/IEC 14882", "C# – ISO/IEC 23270", "C/AL", "Caché ObjectScript", "C Shell", "Caml", "Cayenne", "CDuce", "Cecil", "Cesil", "Céu", "Ceylon", "CFEngine", "CFML", "Cg", "Ch", "Chapel", "Charity", "Charm", "Chef", "CHILL", "CHIP-8", "chomski", "ChucK", "CICS", "Cilk", "Citrine (programming language)", "CL (IBM)", "Claire", "Clarion", "Clean", "Clipper", "CLIPS", "CLIST", "Clojure", "CLU", "CMS-2", "COBOL – ISO/IEC 1989", "CobolScript – COBOL Scripting language", "Cobra", "CODE", "CoffeeScript", "ColdFusion", "COMAL", "Combined Programming Language (CPL)", "COMIT", "Common Intermediate Language (CIL)", "Common Lisp (also known as CL)", "COMPASS", "Component Pascal", "Constraint Handling Rules (CHR)", "COMTRAN", "Converge", "Cool", "Coq", "Coral 66", "Corn", "CorVision", "COWSEL", "CPL", "CPL", "Cryptol", "csh", "Csound", "CSP", "CUDA", "Curl", "Curry", "Cybil", "Cyclone", "Cython", "Java", "Javascript", "M2001", "M4", "M#", "Machine code", "MAD (Michigan Algorithm Decoder)", "MAD/I", "Magik", "Magma", "make", "Maple", "MAPPER now part of BIS", "MARK-IV now VISION:BUILDER", "Mary", "MASM Microsoft Assembly x86", "MATH-MATIC", "Mathematica", "MATLAB", "Maxima (see also Macsyma)", "Max (Max Msp – Graphical Programming Environment)", "Maya (MEL)", "MDL", "Mercury", "Mesa", "Metafont", "Microcode", "MicroScript", "MIIS", "Milk (programming language)", "MIMIC", "Mirah", "Miranda", "MIVA Script", "ML", "Model 204", "Modelica", "Modula", "Modula-2", "Modula-3", "Mohol", "MOO", "Mortran", "Mouse", "MPD", "Mathcad", "MSIL – deprecated name for CIL", "MSL", "MUMPS", "Mystic Programming L"],
        maxTags: 10,
        dropdown: {
            maxItems: 20,           // <- mixumum allowed rendered suggestions
            classname: 'tags-look', // <- custom classname for this dropdown, so it could be targeted
            enabled: 0,             // <- show suggestions on focus
            closeOnSelect: false    // <- do not hide the suggestions dropdown once an item has been selected
        }
    })
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-textarea'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-textarea'>textarea</a></h2>
        </header>
        

        
                <p>
                    In this example, the field is pre-ocupied with 3 tags, and last tag is <strong>not included</strong> in the whitelist,
                    and will be <em>removed</em> because the <code>enforceWhitelist</code> <em>setting flag</em> is set to <code>true</code>
                </p>
                <p>
                    🚩 This example is very interesting because it shows another layer of complexity - Films' names might include commas (<code>,</code>),
                    and therefore it is <strong>very important</strong> to load initial values as Object <em>Collection</em> (Array of Objects)
                    where each Object is proper JSON as shown in the example below, or else initial tags will not get parsed well if they include commas.
                </p>
                <p>
                    🚩 <strong>Another important</strong> thing to keep in mind is that the default <code>delimiters</code>
                    setting of Tagify is to split tags by commas, and when tags (might) contain commas inside them,
                    this setting should be changed to <code>null</code>, so when a user is typing, for example,
                    a movie name which contains commans, a tag will <em>not</em> be created as soon as comma was inputed.
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;textarea name=&#39;tags2&#39; placeholder=&#39;Movie names&#39;&gt; [{&quot;value&quot;:&quot;The Good, the Bad and the Ugly&quot;}, {&quot;value&quot;:&quot;The Matrix&quot;}]&lt;/textarea&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('textarea[name=tags2]'),
    tagify = new Tagify(input, {
        enforceWhitelist : true,
        delimiters       : null,
        whitelist        : ["The Shawshank Redemption", "The Godfather", "The Godfather: Part II", "The Dark Knight", "12 Angry Men", "Schindler's List", "Pulp Fiction", "The Lord of the Rings: The Return of the King", "The Good, the Bad and the Ugly", "Fight Club", "The Lord of the Rings: The Fellowship of the Ring", "Star Wars: Episode V - The Empire Strikes Back", "Forrest Gump", "Inception", "The Lord of the Rings: The Two Towers", "One Flew Over the Cuckoo's Nest", "Goodfellas", "The Matrix", "Seven Samurai", "Star Wars: Episode IV - A New Hope", "City of God", "Se7en", "The Silence of the Lambs", "It's a Wonderful Life", "The Usual Suspects", "Life Is Beautiful", "Léon: The Professional", "Spirited Away", "Saving Private Ryan", "La La Land", "Once Upon a Time in the West", "American History X", "Interstellar", "Casablanca", "Psycho", "City Lights", "The Green Mile", "Raiders of the Lost Ark", "The Intouchables", "Modern Times", "Rear Window", "The Pianist", "The Departed", "Terminator 2: Judgment Day", "Back to the Future", "Whiplash", "Gladiator", "Memento", "Apocalypse Now", "The Prestige", "The Lion King", "Alien", "Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb", "Sunset Boulevard", "The Great Dictator", "Cinema Paradiso", "The Lives of Others", "Paths of Glory", "Grave of the Fireflies", "Django Unchained", "The Shining", "WALL·E", "American Beauty", "The Dark Knight Rises", "Princess Mononoke", "Aliens", "Oldboy", "Once Upon a Time in America", "Citizen Kane", "Das Boot", "Witness for the Prosecution", "North by Northwest", "Vertigo", "Star Wars: Episode VI - Return of the Jedi", "Reservoir Dogs", "M", "Braveheart", "Amélie", "Requiem for a Dream", "A Clockwork Orange", "Taxi Driver", "Lawrence of Arabia", "Like Stars on Earth", "Double Indemnity", "To Kill a Mockingbird", "Eternal Sunshine of the Spotless Mind", "Toy Story 3", "Amadeus", "My Father and My Son", "Full Metal Jacket", "The Sting", "2001: A Space Odyssey", "Singin' in the Rain", "Bicycle Thieves", "Toy Story", "Dangal", "The Kid", "Inglourious Basterds", "Snatch", "Monty Python and the Holy Grail", "Hacksaw Ridge", "3 Idiots", "L.A. Confidential", "For a Few Dollars More", "Scarface", "Rashomon", "The Apartment", "The Hunt", "Good Will Hunting", "Indiana Jones and the Last Crusade", "A Separation", "Metropolis", "Yojimbo", "All About Eve", "Batman Begins", "Up", "Some Like It Hot", "The Treasure of the Sierra Madre", "Unforgiven", "Downfall", "Raging Bull", "The Third Man", "Die Hard", "Children of Heaven", "The Great Escape", "Heat", "Chinatown", "Inside Out", "Pan's Labyrinth", "Ikiru", "My Neighbor Totoro", "On the Waterfront", "Room", "Ran", "The Gold Rush", "The Secret in Their Eyes", "The Bridge on the River Kwai", "Blade Runner", "Mr. Smith Goes to Washington", "The Seventh Seal", "Howl's Moving Castle", "Lock, Stock and Two Smoking Barrels", "Judgment at Nuremberg", "Casino", "The Bandit", "Incendies", "A Beautiful Mind", "A Wednesday", "The General", "The Elephant Man", "Wild Strawberries", "Arrival", "V for Vendetta", "Warrior", "The Wolf of Wall Street", "Manchester by the Sea", "Sunrise", "The Passion of Joan of Arc", "Gran Torino", "Rang De Basanti", "Trainspotting", "Dial M for Murder", "The Big Lebowski", "The Deer Hunter", "Tokyo Story", "Gone with the Wind", "Fargo", "Finding Nemo", "The Sixth Sense", "The Thing", "Hera Pheri", "Cool Hand Luke", "Andaz Apna Apna", "Rebecca", "No Country for Old Men", "How to Train Your Dragon", "Munna Bhai M.B.B.S.", "Sholay", "Kill Bill: Vol. 1", "Into the Wild", "Mary and Max", "Gone Girl", "There Will Be Blood", "Come and See", "It Happened One Night", "Life of Brian", "Rush", "Hotel Rwanda", "Platoon", "Shutter Island", "Network", "The Wages of Fear", "Stand by Me", "Wild Tales", "In the Name of the Father", "Spotlight", "Star Wars: The Force Awakens", "The Nights of Cabiria", "The 400 Blows", "Butch Cassidy and the Sundance Kid", "Mad Max: Fury Road", "The Maltese Falcon", "12 Years a Slave", "Ben-Hur", "The Grand Budapest Hotel", "Persona", "Million Dollar Baby", "Amores Perros", "Jurassic Park", "The Princess Bride", "Hachi: A Dog's Tale", "Memories of Murder", "Stalker", "Nausicaä of the Valley of the Wind", "Drishyam", "The Truman Show", "The Grapes of Wrath", "Before Sunrise", "Touch of Evil", "Annie Hall", "The Message", "Rocky", "Gandhi", "Harry Potter and the Deathly Hallows: Part 2", "The Bourne Ultimatum", "Diabolique", "Donnie Darko", "Monsters, Inc.", "Prisoners", "8½", "The Terminator", "The Wizard of Oz", "Catch Me If You Can", "Groundhog Day", "Twelve Monkeys", "Zootopia", "La Haine", "Barry Lyndon", "Jaws", "The Best Years of Our Lives", "Infernal Affairs", "Udaan", "The Battle of Algiers", "Strangers on a Train", "Dog Day Afternoon", "Sin City", "Kind Hearts and Coronets", "Gangs of Wasseypur", "The Help"],
        callbacks        : {
            add    : console.log,  // callback when adding a tag
            remove : console.log   // callback when removing a tag
        }
    })</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/dyMoYKb'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <textarea name='tags2' placeholder='Movie names'> [{"value":"The Good, the Bad and the Ugly"}, {"value":"The Matrix"}]</textarea>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('textarea[name=tags2]'),
    tagify = new Tagify(input, {
        enforceWhitelist : true,
        delimiters       : null,
        whitelist        : ["The Shawshank Redemption", "The Godfather", "The Godfather: Part II", "The Dark Knight", "12 Angry Men", "Schindler's List", "Pulp Fiction", "The Lord of the Rings: The Return of the King", "The Good, the Bad and the Ugly", "Fight Club", "The Lord of the Rings: The Fellowship of the Ring", "Star Wars: Episode V - The Empire Strikes Back", "Forrest Gump", "Inception", "The Lord of the Rings: The Two Towers", "One Flew Over the Cuckoo's Nest", "Goodfellas", "The Matrix", "Seven Samurai", "Star Wars: Episode IV - A New Hope", "City of God", "Se7en", "The Silence of the Lambs", "It's a Wonderful Life", "The Usual Suspects", "Life Is Beautiful", "Léon: The Professional", "Spirited Away", "Saving Private Ryan", "La La Land", "Once Upon a Time in the West", "American History X", "Interstellar", "Casablanca", "Psycho", "City Lights", "The Green Mile", "Raiders of the Lost Ark", "The Intouchables", "Modern Times", "Rear Window", "The Pianist", "The Departed", "Terminator 2: Judgment Day", "Back to the Future", "Whiplash", "Gladiator", "Memento", "Apocalypse Now", "The Prestige", "The Lion King", "Alien", "Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb", "Sunset Boulevard", "The Great Dictator", "Cinema Paradiso", "The Lives of Others", "Paths of Glory", "Grave of the Fireflies", "Django Unchained", "The Shining", "WALL·E", "American Beauty", "The Dark Knight Rises", "Princess Mononoke", "Aliens", "Oldboy", "Once Upon a Time in America", "Citizen Kane", "Das Boot", "Witness for the Prosecution", "North by Northwest", "Vertigo", "Star Wars: Episode VI - Return of the Jedi", "Reservoir Dogs", "M", "Braveheart", "Amélie", "Requiem for a Dream", "A Clockwork Orange", "Taxi Driver", "Lawrence of Arabia", "Like Stars on Earth", "Double Indemnity", "To Kill a Mockingbird", "Eternal Sunshine of the Spotless Mind", "Toy Story 3", "Amadeus", "My Father and My Son", "Full Metal Jacket", "The Sting", "2001: A Space Odyssey", "Singin' in the Rain", "Bicycle Thieves", "Toy Story", "Dangal", "The Kid", "Inglourious Basterds", "Snatch", "Monty Python and the Holy Grail", "Hacksaw Ridge", "3 Idiots", "L.A. Confidential", "For a Few Dollars More", "Scarface", "Rashomon", "The Apartment", "The Hunt", "Good Will Hunting", "Indiana Jones and the Last Crusade", "A Separation", "Metropolis", "Yojimbo", "All About Eve", "Batman Begins", "Up", "Some Like It Hot", "The Treasure of the Sierra Madre", "Unforgiven", "Downfall", "Raging Bull", "The Third Man", "Die Hard", "Children of Heaven", "The Great Escape", "Heat", "Chinatown", "Inside Out", "Pan's Labyrinth", "Ikiru", "My Neighbor Totoro", "On the Waterfront", "Room", "Ran", "The Gold Rush", "The Secret in Their Eyes", "The Bridge on the River Kwai", "Blade Runner", "Mr. Smith Goes to Washington", "The Seventh Seal", "Howl's Moving Castle", "Lock, Stock and Two Smoking Barrels", "Judgment at Nuremberg", "Casino", "The Bandit", "Incendies", "A Beautiful Mind", "A Wednesday", "The General", "The Elephant Man", "Wild Strawberries", "Arrival", "V for Vendetta", "Warrior", "The Wolf of Wall Street", "Manchester by the Sea", "Sunrise", "The Passion of Joan of Arc", "Gran Torino", "Rang De Basanti", "Trainspotting", "Dial M for Murder", "The Big Lebowski", "The Deer Hunter", "Tokyo Story", "Gone with the Wind", "Fargo", "Finding Nemo", "The Sixth Sense", "The Thing", "Hera Pheri", "Cool Hand Luke", "Andaz Apna Apna", "Rebecca", "No Country for Old Men", "How to Train Your Dragon", "Munna Bhai M.B.B.S.", "Sholay", "Kill Bill: Vol. 1", "Into the Wild", "Mary and Max", "Gone Girl", "There Will Be Blood", "Come and See", "It Happened One Night", "Life of Brian", "Rush", "Hotel Rwanda", "Platoon", "Shutter Island", "Network", "The Wages of Fear", "Stand by Me", "Wild Tales", "In the Name of the Father", "Spotlight", "Star Wars: The Force Awakens", "The Nights of Cabiria", "The 400 Blows", "Butch Cassidy and the Sundance Kid", "Mad Max: Fury Road", "The Maltese Falcon", "12 Years a Slave", "Ben-Hur", "The Grand Budapest Hotel", "Persona", "Million Dollar Baby", "Amores Perros", "Jurassic Park", "The Princess Bride", "Hachi: A Dog's Tale", "Memories of Murder", "Stalker", "Nausicaä of the Valley of the Wind", "Drishyam", "The Truman Show", "The Grapes of Wrath", "Before Sunrise", "Touch of Evil", "Annie Hall", "The Message", "Rocky", "Gandhi", "Harry Potter and the Deathly Hallows: Part 2", "The Bourne Ultimatum", "Diabolique", "Donnie Darko", "Monsters, Inc.", "Prisoners", "8½", "The Terminator", "The Wizard of Oz", "Catch Me If You Can", "Groundhog Day", "Twelve Monkeys", "Zootopia", "La Haine", "Barry Lyndon", "Jaws", "The Best Years of Our Lives", "Infernal Affairs", "Udaan", "The Battle of Algiers", "Strangers on a Train", "Dog Day Afternoon", "Sin City", "Kind Hearts and Coronets", "Gangs of Wasseypur", "The Help"],
        callbacks        : {
            add    : console.log,  // callback when adding a tag
            remove : console.log   // callback when removing a tag
        }
    })
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-rtl'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-rtl'>RTL &amp; uncapped dropdown width</a></h2>
        </header>
        

        
                <p>
                    In this example, the <em>Tagify</em> field is inside a container which has <code>dir='rtl'</code> attribute and
                    also the Tagify <code>RTL</code> setting for the <code>dropdown</code> is set accordingly
                    (because it's better not infer it from the HTML but defined explicitly)
                </p>
                <p>
                    This example also showcases the ability to have the suggestions dropdown's maximum <code>width</code>
                    as wide as the longest item in the suggestions list, but still not less wide than the Taigy field
                    for which the dropdown "belongs".
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;div dir=&#39;rtl&#39;&gt;
    &lt;input name=&#39;rtl-example&#39; class=&#39;tagify--rtl-exmaple&#39; placeholder=&#39;לחץ בשדה והוסף ערך&#39;/&gt;
&lt;/div&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>// The DOM element you wish to replace with Tagify
var input = document.querySelector('input[name=rtl-example]');

// initialize Tagify on the above input node reference
new Tagify(input, {
    whitelist: [
        { value: "מיכאל כהן", full: "<em>מיכאל כהן</em> - פיתוח תוכנה מתקדם ויישום טכנולוגיות חדשניות בתחום התעשייה והייצור" },
        { value: "שרה לוי", full: "<em>שרה לוי</em> - ניהול ופיתוח פתרונות אקולוגיים וסביבתיים למתן יתרון תחרותי לעסקים" },
        { value: "אברהם גולן", full: "<em>אברהם גולן</em> - יישום ופיתוח טכנולוגיות מתקדמות לשיפור פרודוקטיביות ויצירתיות בארגונים" },
        { value: "רחל רביבו", full: "<em>רחל רביבו</em> - מחקר ופיתוח טכנולוגי בתחום החדשנות והיזמות לקידום עסקים ותעשיות" },
        { value: "דוד כהן", full: "<em>דוד כהן</em> - פיתוח ויישום טכנולוגיות מתקדמות לשיפור תשתיות מידע עסקיות" },
        { value: "רבקה אריאל", full: "<em>רבקה אריאל</em> - ייזום ופיתוח מוצרים חדשניים עבור תעשיות יצירתיות ומתקדמות" }
    ],
    dropdown: {
        mapValueTo: 'full',
        classname: 'tagify__dropdown--rtl-example',
        enabled: 0, // shows the suggestiosn dropdown once field is focused
        RTL: true,
        escapeHTML: false // allows HTML inside each suggestion item
    }
})</code></pre>
            
            <h3>CSS</h3>
            <style contenteditable>.tagify__dropdown--rtl-example {
    max-width: none !important;
}

/* each suggestion item in the dropdown should be rendered as a single line of text */
.tagify__dropdown--rtl-example .tagify__dropdown__item {
    white-space: nowrap;
}

/* just for fun */
.tagify__dropdown--rtl-example .tagify__dropdown__item > em {
    font-weight: 700;
}

/* in this example the tagify itself is narrower than the other examples */
.tagify--rtl-exmaple {
    min-width: 300px;
}</style>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/VwReGKL'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <div dir='rtl'>
    <input name='rtl-example' class='tagify--rtl-exmaple' placeholder='לחץ בשדה והוסף ערך'/>
</div>
    </aside>

    <script>
        (function(){
        // The DOM element you wish to replace with Tagify
var input = document.querySelector('input[name=rtl-example]');

// initialize Tagify on the above input node reference
new Tagify(input, {
    whitelist: [
        { value: "מיכאל כהן", full: "<em>מיכאל כהן</em> - פיתוח תוכנה מתקדם ויישום טכנולוגיות חדשניות בתחום התעשייה והייצור" },
        { value: "שרה לוי", full: "<em>שרה לוי</em> - ניהול ופיתוח פתרונות אקולוגיים וסביבתיים למתן יתרון תחרותי לעסקים" },
        { value: "אברהם גולן", full: "<em>אברהם גולן</em> - יישום ופיתוח טכנולוגיות מתקדמות לשיפור פרודוקטיביות ויצירתיות בארגונים" },
        { value: "רחל רביבו", full: "<em>רחל רביבו</em> - מחקר ופיתוח טכנולוגי בתחום החדשנות והיזמות לקידום עסקים ותעשיות" },
        { value: "דוד כהן", full: "<em>דוד כהן</em> - פיתוח ויישום טכנולוגיות מתקדמות לשיפור תשתיות מידע עסקיות" },
        { value: "רבקה אריאל", full: "<em>רבקה אריאל</em> - ייזום ופיתוח מוצרים חדשניים עבור תעשיות יצירתיות ומתקדמות" }
    ],
    dropdown: {
        mapValueTo: 'full',
        classname: 'tagify__dropdown--rtl-example',
        enabled: 0, // shows the suggestiosn dropdown once field is focused
        RTL: true,
        escapeHTML: false // allows HTML inside each suggestion item
    }
})
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-different-look'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-different-look'>Easy to customize</a></h2>
        </header>
        

        
                <p>
                    The easiest way to customize styles <em>(colors, borders, spacing, radii, etc.)</em> is by using <a href='https://github.com/yairEO/tagify#css-variables'>CSS variables</a>.
                </p>
                <p>
                    No placeholder, and no way of adding tags from within the component, but only
                    by clicking the (+) button, which is not related to Tagify in anyway, but in this example
                    it is shown how combining a few simple things make customization easy.
                </p>
                <p>
                    (This example also shows how to implement minimum-allowed tags)
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input class=&#39;customLook&#39; value=&#39;some.name@website.com&#39;&gt;&lt;button type=&quot;button&quot;&gt;+&lt;/button&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>// generate random whilist items (for the demo)
var randomStringsArr = Array.apply(null, Array(100)).map(function () {
    return Array.apply(null, Array(~~(Math.random() * 10 + 3))).map(function () {
        return String.fromCharCode(Math.random() * (123 - 97) + 97)
    }).join('') + '@gmail.com'
})

var input = document.querySelector('.customLook'),
    button = input.nextElementSibling,
    tagify = new Tagify(input, {
        editTags: {
            keepInvalid: false, // better to auto-remove invalid tags which are in edit-mode (on blur)
        },
        // email address validation (https://stackoverflow.com/a/46181/104380)
        pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
        whitelist: randomStringsArr,
        callbacks: {
            "invalid": onInvalidTag
        },
        dropdown: {
            position: 'text',
            enabled: 1 // show suggestions dropdown after 1 typed character
        }
    });  // "add new tag" action-button

button.addEventListener("click", onAddButtonClick)

function onAddButtonClick() {
    tagify.addEmptyTag()
}

function onInvalidTag(e) {
    console.log("invalid", e.detail)
}</code></pre>
            
            <h3>CSS</h3>
            <style contenteditable>.customLook {
    --tag-bg                  : #0052BF;
    --tag-hover               : #CE0078;
    --tag-text-color          : #FFF;
    --tags-border-color       : silver;
    --tag-text-color--edit    : #111;
    --tag-remove-bg           : var(--tag-hover);
    --tag-pad                 : .6em 1em;
    --tag-inset-shadow-size   : 1.4em; /* compensate for the larger --tag-pad value */
    --tag-remove-btn-color    : white;
    --tag-remove-btn-bg--hover: black;

    display: inline-block;
    min-width: 0;
    border: none;
}

.customLook .tagify__tag {
    margin-top: 0;
}

.customLook .tagify__tag>div {
    border-radius: 25px;
}

.customLook .tagify__tag:not(:only-of-type):not(.tagify__tag--editable):hover .tagify__tag-text {
    margin-inline-end: -1px;
}

/* Do not show the "remove tag" (x) button when only a single tag remains */
.customLook .tagify__tag:only-of-type .tagify__tag__removeBtn {
    display: none;
}

.customLook .tagify__tag__removeBtn {
    opacity: 0;
    transform: translateX(-100%) scale(.5);
    margin-inline: -20px 6px;
    /* very specific on purpose  */
    text-align: right;
    transition: .12s;
}

.customLook .tagify__tag:not(.tagify__tag--editable):hover .tagify__tag__removeBtn {
    transform: none;
    opacity: 1;
}

.customLook+button {
    color: #0052BF;
    font: bold 1.4em/1.65 Arial;
    border: 0;
    background: none;
    box-shadow: 0 0 0 2px inset currentColor;
    border-radius: 50%;
    width: 1.65em;
    height: 1.65em;
    cursor: pointer;
    outline: none;
    transition: .1s ease-out;
    margin: 0 0 0 5px;
    vertical-align: top;
}

.customLook+button:hover {
    box-shadow: 0 0 0 5px inset currentColor;
}

.customLook .tagify__input {
    display: none;
}</style>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/wvGaKxd'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input class='customLook' value='some.name@website.com'><button type="button">+</button>
    </aside>

    <script>
        (function(){
        // generate random whilist items (for the demo)
var randomStringsArr = Array.apply(null, Array(100)).map(function () {
    return Array.apply(null, Array(~~(Math.random() * 10 + 3))).map(function () {
        return String.fromCharCode(Math.random() * (123 - 97) + 97)
    }).join('') + '@gmail.com'
})

var input = document.querySelector('.customLook'),
    button = input.nextElementSibling,
    tagify = new Tagify(input, {
        editTags: {
            keepInvalid: false, // better to auto-remove invalid tags which are in edit-mode (on blur)
        },
        // email address validation (https://stackoverflow.com/a/46181/104380)
        pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
        whitelist: randomStringsArr,
        callbacks: {
            "invalid": onInvalidTag
        },
        dropdown: {
            position: 'text',
            enabled: 1 // show suggestions dropdown after 1 typed character
        }
    });  // "add new tag" action-button

button.addEventListener("click", onAddButtonClick)

function onAddButtonClick() {
    tagify.addEmptyTag()
}

function onInvalidTag(e) {
    console.log("invalid", e.detail)
}
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-mix'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-mix'>Mix text &amp; tags</a></h2>
        </header>
        

        
                <p>
                    It is possible to configure Tagify to supoprt mixed content.
                    A common example would be <em>tagging</em> people while writing a comment on a social website.
                </p>
                <p>
                    To allow mix content, simply pass the setting <code>mode : 'mix'</code>
                </p>
                <p>
                    In this example, there are a few <em>Southpark</em> character names and a few <em>Simpsons</em> ones.
                    when <code>@</code> is typed, following 1 more character, suggestions dropdown will show <em>Southpark</em>-only items,
                    and when starting with <code>#</code>, <i>Simpsons</i> items list.
                </p>
                <p>
                    Note that the two whitelists are very different from eachother in teams of structure. The first is quite complex,
                    as it uses numeric IDs as <code>value</code> ("value" prop is always unique), and the rendered text is placed in a custom
                    property named "text". <br>
                    The other whitelist, <em>Simpsons</em> is very basic, just a flat array of strings.
                </p>
                <p>
                    Note that "homer simpson" tag is set to <code>readonly</code> in this example. If tried to select all text and delete
                    everything, <em>readonly</em> tags will remain.
                </p>
                <p>
                    When a <code>textarea</code> already has mixed-content and it wasn't pre-configured using a whitelist, the tags will ignored, so keep
                    that in mind when populating a textarea with server data and expecting tags to "magically" appear. Whitelist <strong><em>must</em></strong> exist because not
                    everything that starts with <code>@</code> or <code>#</code> (or whatever was defined as <code>pattern</code>) is meant to be rendered as a tag.
                </p>
                <p>
                    This example also validates the tags using the <code>validate</code> setting with a function as a value.
                </p>
                <h4>Creating new tags:</h4>
                <p>
                    To create new tags (ones not included in your <code>whitelist</code> collection), simply make sure
                    to keep the <code>enforceWhitelist</code> setting as <code>false</code>, which is the <em>default</em>.
                    After a new tags has been added, you should listen to the <code>add</code> and probably update your local state (Ex. server request).
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;textarea name=&#39;mix&#39;&gt;[[{&quot;id&quot;:101, &quot;value&quot;:&quot;cartman&quot;, &quot;title&quot;:&quot;Eric Cartman&quot;}]] and [[kyle]] do not know [[{&quot;value&quot;:&quot;homer simpson&quot;, &quot;readonly&quot;:true}]] because he&#39;s a relic.&lt;/textarea&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>// Define two types of whitelists, each used for the dropdown suggestions menu,
// depending on the prefix pattern typed (@/#). See settings below.
var whitelist_1 = [
    { value: 100, text: 'kenny', title: 'Kenny McCormick' },
    { value: 200, text: 'cartman', title: 'Eric Cartman' },
    { value: 300, text: 'kyle', title: 'Kyle Broflovski' },
    { value: 400, text: 'token', title: 'Token Black' },
    { value: 500, text: 'jimmy', title: 'Jimmy Valmer' },
    { value: 600, text: 'butters', title: 'Butters Stotch' },
    { value: 700, text: 'stan', title: 'Stan Marsh' },
    { value: 800, text: 'randy', title: 'Randy Marsh' },
    { value: 900, text: 'Mr. Garrison', title: 'POTUS' },
    { value: 1000, text: 'Mr. Mackey', title: "M'Kay" }
  ]

// Second whitelist, which is shown only when starting to type "#".
// Below whitelist is the simplest possible format.
var whitelist_2 = ['Homer simpson', 'Marge simpson', 'Bart', 'Lisa', 'Maggie', 'Mr. Burns', 'Ned', 'Milhouse', 'Moe'];


// initialize Tagify
var input = document.querySelector('[name=mix]'),
    // init Tagify script on the above inputs
    tagify = new Tagify(input, {
    //  mixTagsInterpolator: [", "],
        mode: 'mix',  // <--  Enable mixed-content
        pattern: /@|#/,  // <--  Text starting with @ or # (if single, String can be used here)
        tagTextProp: 'text',  // <-- the default property (from whitelist item) for the text to be rendered in a tag element.
        // Array for initial interpolation, which allows only these tags to be used
        whitelist: whitelist_1.concat(whitelist_2).map(function(item){
            return typeof item == 'string' ? {value:item} : item
        }),

        // custom validation - no special characters
        validate(data){
            return !/[^a-zA-Z0-9 ]/.test(data.value)
        },

        dropdown : {
            enabled: 1,
            position: 'text', // <-- render the suggestions list next to the typed text ("caret")
            mapValueTo: 'text', // <-- similar to above "tagTextProp" setting, but for the dropdown items
            highlightFirst: true  // automatically highlights first sugegstion item in the dropdown
        },
        callbacks: {
            add: console.log,  // callback when adding a tag
            remove: console.log   // callback when removing a tag
        }
    })


// A good place to pull server suggestion list accoring to the prefix/value
tagify.on('input', function(e){
    var prefix = e.detail.prefix;

    // first, clean the whitlist array, because the below code, while not, might be async,
    // therefore it should be up to you to decide WHEN to render the suggestions dropdown
    // tagify.settings.whitelist.length = 0;

    if( prefix ){
        if( prefix == '@' )
            tagify.whitelist = whitelist_1;

        if( prefix == '#' )
            tagify.whitelist = whitelist_2;

        if( e.detail.value.length > 1 )
            tagify.dropdown.show(e.detail.value);
    }

    console.log( tagify.value )
    console.log('mix-mode "input" event value: ', e.detail)
})

tagify.on('add', function(e){
    console.log(e)
})</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/KKzpdxQ'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <textarea name='mix'>[[{"id":101, "value":"cartman", "title":"Eric Cartman"}]] and [[kyle]] do not know [[{"value":"homer simpson", "readonly":true}]] because he's a relic.</textarea>
    </aside>

    <script>
        (function(){
        // Define two types of whitelists, each used for the dropdown suggestions menu,
// depending on the prefix pattern typed (@/#). See settings below.
var whitelist_1 = [
    { value: 100, text: 'kenny', title: 'Kenny McCormick' },
    { value: 200, text: 'cartman', title: 'Eric Cartman' },
    { value: 300, text: 'kyle', title: 'Kyle Broflovski' },
    { value: 400, text: 'token', title: 'Token Black' },
    { value: 500, text: 'jimmy', title: 'Jimmy Valmer' },
    { value: 600, text: 'butters', title: 'Butters Stotch' },
    { value: 700, text: 'stan', title: 'Stan Marsh' },
    { value: 800, text: 'randy', title: 'Randy Marsh' },
    { value: 900, text: 'Mr. Garrison', title: 'POTUS' },
    { value: 1000, text: 'Mr. Mackey', title: "M'Kay" }
  ]

// Second whitelist, which is shown only when starting to type "#".
// Below whitelist is the simplest possible format.
var whitelist_2 = ['Homer simpson', 'Marge simpson', 'Bart', 'Lisa', 'Maggie', 'Mr. Burns', 'Ned', 'Milhouse', 'Moe'];


// initialize Tagify
var input = document.querySelector('[name=mix]'),
    // init Tagify script on the above inputs
    tagify = new Tagify(input, {
    //  mixTagsInterpolator: [", "],
        mode: 'mix',  // <--  Enable mixed-content
        pattern: /@|#/,  // <--  Text starting with @ or # (if single, String can be used here)
        tagTextProp: 'text',  // <-- the default property (from whitelist item) for the text to be rendered in a tag element.
        // Array for initial interpolation, which allows only these tags to be used
        whitelist: whitelist_1.concat(whitelist_2).map(function(item){
            return typeof item == 'string' ? {value:item} : item
        }),

        // custom validation - no special characters
        validate(data){
            return !/[^a-zA-Z0-9 ]/.test(data.value)
        },

        dropdown : {
            enabled: 1,
            position: 'text', // <-- render the suggestions list next to the typed text ("caret")
            mapValueTo: 'text', // <-- similar to above "tagTextProp" setting, but for the dropdown items
            highlightFirst: true  // automatically highlights first sugegstion item in the dropdown
        },
        callbacks: {
            add: console.log,  // callback when adding a tag
            remove: console.log   // callback when removing a tag
        }
    })


// A good place to pull server suggestion list accoring to the prefix/value
tagify.on('input', function(e){
    var prefix = e.detail.prefix;

    // first, clean the whitlist array, because the below code, while not, might be async,
    // therefore it should be up to you to decide WHEN to render the suggestions dropdown
    // tagify.settings.whitelist.length = 0;

    if( prefix ){
        if( prefix == '@' )
            tagify.whitelist = whitelist_1;

        if( prefix == '#' )
            tagify.whitelist = whitelist_2;

        if( e.detail.value.length > 1 )
            tagify.dropdown.show(e.detail.value);
    }

    console.log( tagify.value )
    console.log('mix-mode "input" event value: ', e.detail)
})

tagify.on('add', function(e){
    console.log(e)
})
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-outside-of-the-box'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-outside-of-the-box'>outside-of-the-box</a></h2>
        </header>
        

        
                <p>
                    Some designs might require showing the tags outside of the input box, so to set this up
                    some manual CSS changes has to be made, and also the <code>dropdown.position</code> <em>setting</em>
                    should be set to <code>"input"</code> so the suggestions dropdown will be rendered right next to <em>input</em>
                    element and not relative to the whole component (which is the default)
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input name=&#39;tags-outside&#39; class=&#39;tagify--outside&#39; value=&#39;tag1, tag2, tag3&#39; placeholder=&#39;write tags to add below&#39;&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input[name=tags-outside]')

var tagify = new Tagify(input, {
    whitelist: ['foo', 'bar', 'baz'],
    focusable: false,
    dropdown: {
        position: 'input',
        enabled: 0 // always opens dropdown when input gets focus
    }
})</code></pre>
            
            <h3>CSS</h3>
            <style contenteditable>.tagify--outside {
    border: 0;
}

.tagify--outside .tagify__input {
    order: -1;
    flex: 100%;
    border: 1px solid var(--tags-border-color);
    margin-bottom: 1em;
    transition: .1s;
}

.tagify--outside .tagify__input:hover {
    border-color: var(--tags-hover-border-color);
}

.tagify--outside.tagify--focus .tagify__input {
    transition: 0s;
    border-color: var(--tags-focus-border-color);
}</style>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/oNxXjav'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input name='tags-outside' class='tagify--outside' value='tag1, tag2, tag3' placeholder='write tags to add below'>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input[name=tags-outside]')

var tagify = new Tagify(input, {
    whitelist: ['foo', 'bar', 'baz'],
    focusable: false,
    dropdown: {
        position: 'input',
        enabled: 0 // always opens dropdown when input gets focus
    }
})
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-manual-suggestions'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-manual-suggestions'>Render suggestions list manually</a></h2>
        </header>
        

        
                <p>
                    Renders the suggestions list <strong>manually</strong> - useful for situations where it is wished showing the suggestions list
                    inside a modal or your own popup implementaion, or even always show it. In this example the list is always shown.
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input name=&#39;tags-manual-suggestions&#39; placeholder=&#39;write some tags&#39;&gt;
&lt;h3&gt;☝ Add items from below list:&lt;/h3&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input[name=tags-manual-suggestions]'),
    // init Tagify script on the above inputs
    tagify = new Tagify(input, {
        whitelist: ["A# .NET", "A# (Axiom)", "A-0 System", "A+", "A++", "ABAP", "ABC", "ABC ALGOL", "ABSET", "ABSYS", "ACC", "Accent", "Ace DASL", "ACL2", "Avicsoft", "ACT-III", "Action!", "ActionScript", "Ada", "Adenine", "Agda", "Agilent VEE", "Agora", "AIMMS", "Alef", "ALF", "ALGOL 58", "ALGOL 60", "ALGOL 68", "ALGOL W", "Alice", "Alma-0", "AmbientTalk", "Amiga E", "AMOS", "AMPL", "Apex (Salesforce.com)", "APL", "AppleScript", "Arc", "ARexx", "Argus", "AspectJ", "Assembly language", "ATS", "Ateji PX", "AutoHotkey", "Autocoder", "AutoIt", "AutoLISP / Visual LISP", "Averest", "AWK", "Axum", "Active Server Pages", "ASP.NET", "B", "Babbage", "Bash", "BASIC", "bc", "BCPL", "BeanShell", "Batch (Windows/Dos)", "Bertrand", "BETA", "Bigwig", "Bistro", "BitC", "BLISS", "Blockly", "BlooP", "Blue", "Boo", "Boomerang", "Bourne shell (including bash and ksh)", "BREW", "BPEL", "B", "C--", "C++ – ISO/IEC 14882", "C# – ISO/IEC 23270", "C/AL", "Caché ObjectScript", "C Shell", "Caml", "Cayenne", "CDuce", "Cecil", "Cesil", "Céu", "Ceylon", "CFEngine", "CFML", "Cg", "Ch", "Chapel", "Charity", "Charm", "Chef", "CHILL", "CHIP-8", "chomski", "ChucK", "CICS", "Cilk", "Citrine (programming language)", "CL (IBM)", "Claire", "Clarion", "Clean", "Clipper", "CLIPS", "CLIST", "Clojure", "CLU", "CMS-2", "COBOL – ISO/IEC 1989", "CobolScript – COBOL Scripting language", "Cobra", "CODE", "CoffeeScript", "ColdFusion", "COMAL", "Combined Programming Language (CPL)", "COMIT", "Common Intermediate Language (CIL)", "Common Lisp (also known as CL)", "COMPASS", "Component Pascal", "Constraint Handling Rules (CHR)", "COMTRAN", "Converge", "Cool", "Coq", "Coral 66", "Corn", "CorVision", "COWSEL", "CPL", "CPL", "Cryptol", "csh", "Csound", "CSP", "CUDA", "Curl", "Curry", "Cybil", "Cyclone", "Cython", "Java", "Javascript", "M2001", "M4", "M#", "Machine code", "MAD (Michigan Algorithm Decoder)", "MAD/I", "Magik", "Magma", "make", "Maple", "MAPPER now part of BIS", "MARK-IV now VISION:BUILDER", "Mary", "MASM Microsoft Assembly x86", "MATH-MATIC", "Mathematica", "MATLAB", "Maxima (see also Macsyma)", "Max (Max Msp – Graphical Programming Environment)", "Maya (MEL)", "MDL", "Mercury", "Mesa", "Metafont", "Microcode", "MicroScript", "MIIS", "Milk (programming language)", "MIMIC", "Mirah", "Miranda", "MIVA Script", "ML", "Model 204", "Modelica", "Modula", "Modula-2", "Modula-3", "Mohol", "MOO", "Mortran", "Mouse", "MPD", "Mathcad", "MSIL – deprecated name for CIL", "MSL", "MUMPS", "Mystic Programming L"],
        dropdown: {
            position: "manual",
            maxItems: Infinity,
            enabled: 0,
            classname: "customSuggestionsList"
        },
        templates: {
            dropdownItemNoMatch() {
                return `<div class='empty'>Nothing Found</div>`;
            }
        },
        enforceWhitelist: true
    })

tagify.on("dropdown:show", onSuggestionsListUpdate)
      .on("dropdown:hide", onSuggestionsListHide)
      .on('dropdown:scroll', onDropdownScroll)

renderSuggestionsList()  // defined down below

// ES2015 argument destructuring
function onSuggestionsListUpdate({ detail: suggestionsElm }) {
    console.log(suggestionsElm)
}

function onSuggestionsListHide() {
    console.log("hide dropdown")
}

function onDropdownScroll(e) {
    console.log(e.detail)
}

// https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement
function renderSuggestionsList() {
    tagify.dropdown.show() // load the list
    tagify.DOM.scope.parentNode.appendChild(tagify.DOM.dropdown)
}</code></pre>
            
            <h3>CSS</h3>
            <style contenteditable>.customSuggestionsList > div{
    max-height: 300px;
    min-height: 50px;
    border: 2px solid pink;
    overflow: auto;
  }

  .customSuggestionsList .empty{
    color: #999;
    font-size: 20px;
    text-align: center;
    padding: 1em;
  }</style>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/QWNbjZO'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input name='tags-manual-suggestions' placeholder='write some tags'>
<h3>☝ Add items from below list:</h3>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input[name=tags-manual-suggestions]'),
    // init Tagify script on the above inputs
    tagify = new Tagify(input, {
        whitelist: ["A# .NET", "A# (Axiom)", "A-0 System", "A+", "A++", "ABAP", "ABC", "ABC ALGOL", "ABSET", "ABSYS", "ACC", "Accent", "Ace DASL", "ACL2", "Avicsoft", "ACT-III", "Action!", "ActionScript", "Ada", "Adenine", "Agda", "Agilent VEE", "Agora", "AIMMS", "Alef", "ALF", "ALGOL 58", "ALGOL 60", "ALGOL 68", "ALGOL W", "Alice", "Alma-0", "AmbientTalk", "Amiga E", "AMOS", "AMPL", "Apex (Salesforce.com)", "APL", "AppleScript", "Arc", "ARexx", "Argus", "AspectJ", "Assembly language", "ATS", "Ateji PX", "AutoHotkey", "Autocoder", "AutoIt", "AutoLISP / Visual LISP", "Averest", "AWK", "Axum", "Active Server Pages", "ASP.NET", "B", "Babbage", "Bash", "BASIC", "bc", "BCPL", "BeanShell", "Batch (Windows/Dos)", "Bertrand", "BETA", "Bigwig", "Bistro", "BitC", "BLISS", "Blockly", "BlooP", "Blue", "Boo", "Boomerang", "Bourne shell (including bash and ksh)", "BREW", "BPEL", "B", "C--", "C++ – ISO/IEC 14882", "C# – ISO/IEC 23270", "C/AL", "Caché ObjectScript", "C Shell", "Caml", "Cayenne", "CDuce", "Cecil", "Cesil", "Céu", "Ceylon", "CFEngine", "CFML", "Cg", "Ch", "Chapel", "Charity", "Charm", "Chef", "CHILL", "CHIP-8", "chomski", "ChucK", "CICS", "Cilk", "Citrine (programming language)", "CL (IBM)", "Claire", "Clarion", "Clean", "Clipper", "CLIPS", "CLIST", "Clojure", "CLU", "CMS-2", "COBOL – ISO/IEC 1989", "CobolScript – COBOL Scripting language", "Cobra", "CODE", "CoffeeScript", "ColdFusion", "COMAL", "Combined Programming Language (CPL)", "COMIT", "Common Intermediate Language (CIL)", "Common Lisp (also known as CL)", "COMPASS", "Component Pascal", "Constraint Handling Rules (CHR)", "COMTRAN", "Converge", "Cool", "Coq", "Coral 66", "Corn", "CorVision", "COWSEL", "CPL", "CPL", "Cryptol", "csh", "Csound", "CSP", "CUDA", "Curl", "Curry", "Cybil", "Cyclone", "Cython", "Java", "Javascript", "M2001", "M4", "M#", "Machine code", "MAD (Michigan Algorithm Decoder)", "MAD/I", "Magik", "Magma", "make", "Maple", "MAPPER now part of BIS", "MARK-IV now VISION:BUILDER", "Mary", "MASM Microsoft Assembly x86", "MATH-MATIC", "Mathematica", "MATLAB", "Maxima (see also Macsyma)", "Max (Max Msp – Graphical Programming Environment)", "Maya (MEL)", "MDL", "Mercury", "Mesa", "Metafont", "Microcode", "MicroScript", "MIIS", "Milk (programming language)", "MIMIC", "Mirah", "Miranda", "MIVA Script", "ML", "Model 204", "Modelica", "Modula", "Modula-2", "Modula-3", "Mohol", "MOO", "Mortran", "Mouse", "MPD", "Mathcad", "MSIL – deprecated name for CIL", "MSL", "MUMPS", "Mystic Programming L"],
        dropdown: {
            position: "manual",
            maxItems: Infinity,
            enabled: 0,
            classname: "customSuggestionsList"
        },
        templates: {
            dropdownItemNoMatch() {
                return `<div class='empty'>Nothing Found</div>`;
            }
        },
        enforceWhitelist: true
    })

tagify.on("dropdown:show", onSuggestionsListUpdate)
      .on("dropdown:hide", onSuggestionsListHide)
      .on('dropdown:scroll', onDropdownScroll)

renderSuggestionsList()  // defined down below

// ES2015 argument destructuring
function onSuggestionsListUpdate({ detail: suggestionsElm }) {
    console.log(suggestionsElm)
}

function onSuggestionsListHide() {
    console.log("hide dropdown")
}

function onDropdownScroll(e) {
    console.log(e.detail)
}

// https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement
function renderSuggestionsList() {
    tagify.dropdown.show() // load the list
    tagify.DOM.scope.parentNode.appendChild(tagify.DOM.dropdown)
}
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-disabled-user-input'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-disabled-user-input'>disabled-user-input</a></h2>
        </header>
        

        
                <p>
                    User <strong>cannot type</strong> or paste any input not edit already-exiting tags.<br>
                    Tags can only be selected from the suggestions list (<em>whitelist</em>) dropdown.
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input name=&#39;disabled-user-input&#39; placeholder=&#39;Select tags from the list&#39;&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input[name=disabled-user-input]'),
    tagify = new Tagify(input, {
        whitelist: [1,2,3,4,5],
        userInput: false
    })</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/PomMMwp'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input name='disabled-user-input' placeholder='Select tags from the list'>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input[name=disabled-user-input]'),
    tagify = new Tagify(input, {
        whitelist: [1,2,3,4,5],
        userInput: false
    })
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-users-list'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-users-list'>users-list</a></h2>
        </header>
        

        
                <p>
                    This example shows how to customize <em>Tagify</em> further. It also includes an <code>Add All</code> option as the first
                    item in the suggestions dropdown list.
                </p>
                <p>
                    <code>tagTextProp</code> is very interesting in this example as it allows inputing both a <code>value</code>
                    or a <code>name</code> and have them both match against the whitelist items. First the "value" is looked for,
                    and if failed, then <em>prop</em> which was defined by the <code>tagTextProp</code> setting.
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input name=&#39;users-list-tags&#39; class=&#39;users-list&#39; value=&#39;abatisse2@nih.gov, Justinian Hattersley&#39;&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var inputElm = document.querySelector('input[name=users-list-tags]');

function tagTemplate(tagData) {
    return `
        <tag title="${tagData.email}"
                contenteditable='false'
                spellcheck='false'
                tabIndex="-1"
                class="tagify__tag ${tagData.class ? tagData.class : ""}"
                ${this.getAttributes(tagData)}>
            <x title='' class='tagify__tag__removeBtn' role='button' aria-label='remove tag'></x>
            <div>
                <div class='tagify__tag__avatar-wrap'>
                    <img onerror="this.style.visibility='hidden'" src="${tagData.avatar}">
                </div>
                <span class='tagify__tag-text'>${tagData.name}</span>
            </div>
        </tag>
    `
}

function suggestionItemTemplate(tagData) {
    return `
        <div ${this.getAttributes(tagData)}
            class='tagify__dropdown__item ${tagData.class ? tagData.class : ""}'
            tabindex="0"
            role="option">
            ${tagData.avatar ? `
                <div class='tagify__dropdown__item__avatar-wrap'>
                    <img onerror="this.style.visibility='hidden'" src="${tagData.avatar}">
                </div>` : ''
        }
            <strong>${tagData.name}</strong>
            <span>${tagData.email}</span>
        </div>
    `
}

function dropdownHeaderTemplate(suggestions) {
    return `
        <header data-selector='tagify-suggestions-header' class="${this.settings.classNames.dropdownItem} ${this.settings.classNames.dropdownItem}__addAll">
            <strong style='grid-area: add'>${this.value.length ? `Add Remaning` : 'Add All'}</strong>
            <span style='grid-area: remaning'>${suggestions.length} members</span>
            <a class='remove-all-tags'>Remove all</a>
        </header>
    `
}

// initialize Tagify on the above input node reference
var tagify = new Tagify(inputElm, {
    tagTextProp: 'name', // very important since a custom template is used with this property as text
    // enforceWhitelist: true,
    skipInvalid: true, // do not remporarily add invalid tags
    dropdown: {
        closeOnSelect: false,
        enabled: 0,
        classname: 'users-list',
        searchKeys: ['name', 'email']  // very important to set by which keys to search for suggesttions when typing
    },
    templates: {
        tag: tagTemplate,
        dropdownItem: suggestionItemTemplate,
        dropdownHeader: dropdownHeaderTemplate
    },
    whitelist: [
        {
            "value": 1,
            "name": "Justinian Hattersley",
            "avatar": "https://i.pravatar.cc/80?img=1",
            "email": "jhattersley0@ucsd.edu",
            "team": "A"
        },
        {
            "value": 2,
            "name": "Antons Esson",
            "avatar": "https://i.pravatar.cc/80?img=2",
            "email": "aesson1@ning.com",
            "team": "B"

        },
        {
            "value": 3,
            "name": "Ardeen Batisse",
            "avatar": "https://i.pravatar.cc/80?img=3",
            "email": "abatisse2@nih.gov",
            "team": "A"
        },
        {
            "value": 4,
            "name": "Graeme Yellowley",
            "avatar": "https://i.pravatar.cc/80?img=4",
            "email": "gyellowley3@behance.net",
            "team": "C"
        },
        {
            "value": 5,
            "name": "Dido Wilford",
            "avatar": "https://i.pravatar.cc/80?img=5",
            "email": "dwilford4@jugem.jp",
            "team": "A"
        },
        {
            "value": 6,
            "name": "Celesta Orwin",
            "avatar": "https://i.pravatar.cc/80?img=6",
            "email": "corwin5@meetup.com",
            "team": "C"
        },
        {
            "value": 7,
            "name": "Sally Main",
            "avatar": "https://i.pravatar.cc/80?img=7",
            "email": "smain6@techcrunch.com",
            "team": "A"
        },
        {
            "value": 8,
            "name": "Grethel Haysman",
            "avatar": "https://i.pravatar.cc/80?img=8",
            "email": "ghaysman7@mashable.com",
            "team": "B"
        },
        {
            "value": 9,
            "name": "Marvin Mandrake",
            "avatar": "https://i.pravatar.cc/80?img=9",
            "email": "mmandrake8@sourceforge.net",
            "team": "B"
        },
        {
            "value": 10,
            "name": "Corrie Tidey",
            "avatar": "https://i.pravatar.cc/80?img=10",
            "email": "ctidey9@youtube.com",
            "team": "A"
        },
        {
            "value": 11,
            "name": "foo",
            "avatar": "https://i.pravatar.cc/80?img=11",
            "email": "foo@bar.com",
            "team": "B"
        },
        {
            "value": 12,
            "name": "foo",
            "avatar": "https://i.pravatar.cc/80?img=12",
            "email": "foo.aaa@foo.com",
            "team": "A"
        },
    ],

    transformTag: (tagData, originalData) => {
        var { name, email } = parseFullValue(tagData.name)
        tagData.name = name
        tagData.email = email || tagData.email
    },

    validate({ name, email }) {
        // when editing a tag, there will only be the "name" property which contains name + email (see 'transformTag' above)
        if (!email && name) {
            var parsed = parseFullValue(name)
            name = parsed.name
            email = parsed.email
        }

        if (!name) return "Missing name"
        if (!validateEmail(email)) return "Invalid email"

        return true
    }
})

// The below code is printed as escaped, so please copy this function from:
// https://github.com/yairEO/tagify/blob/master/src/parts/helpers.js#L89-L97
function escapeHTML(s) {
    return typeof s == 'string' ? s
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/`|'/g, "&#039;")
        : s;
}

// The below part is only if you want to split the users into groups, when rendering the suggestions list dropdown:
// (since each user also has a 'team' property)
tagify.dropdown.createListHTML = sugegstionsList => {
    const teamsOfUsers = sugegstionsList.reduce((acc, suggestion) => {
        const team = suggestion.team || 'Not Assigned';

        if (!acc[team])
            acc[team] = [suggestion]
        else
            acc[team].push(suggestion)

        return acc
    }, {})

    const getUsersSuggestionsHTML = teamUsers => teamUsers.map((suggestion, idx) => {
        if (typeof suggestion == 'string' || typeof suggestion == 'number')
            suggestion = { value: suggestion }

        var value = tagify.dropdown.getMappedValue.call(tagify, suggestion)

        suggestion.value = value && typeof value == 'string' ? escapeHTML(value) : value

        return tagify.settings.templates.dropdownItem.apply(tagify, [suggestion]);
    }).join("")


    // assign the user to a group
    return Object.entries(teamsOfUsers).map(([team, teamUsers]) => {
        return `<div class="tagify__dropdown__itemsGroup" data-title="Team ${team}:">${getUsersSuggestionsHTML(teamUsers)}</div>`
    }).join("")
}

// attach events listeners
tagify.on('dropdown:select', onSelectSuggestion) // allows selecting all the suggested (whitelist) items
      .on('edit:start', onEditStart)  // show custom text in the tag while in edit-mode

function onSelectSuggestion(e) {
    if (e.detail.event.target.matches('.remove-all-tags')) {
        tagify.removeAllTags()
    }

    // custom class from "dropdownHeaderTemplate"
    else if (e.detail.elm.classList.contains(`${tagify.settings.classNames.dropdownItem}__addAll`))
        tagify.dropdown.selectAll();
}

function onEditStart({ detail: { tag, data } }) {
    tagify.setTagTextNode(tag, `${data.name} <${data.email}>`)
}

// https://stackoverflow.com/a/9204568/104380
function validateEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}

function parseFullValue(value) {
    // https://stackoverflow.com/a/11592042/104380
    var parts = value.split(/<(.*?)>/g),
        name = parts[0].trim(),
        email = parts[1]?.replace(/<(.*?)>/g, '').trim();

    return { name, email }
}</code></pre>
            
            <h3>CSS</h3>
            <style contenteditable>/* Suggestions items */
:root {
    --tagify-dd-item-pad: .5em .7em;
}

.tagify__dropdown.users-list .tagify__dropdown__item {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0 1em;
    grid-template-areas: "avatar name"
        "avatar email";
}

.tagify__dropdown.users-list header.tagify__dropdown__item {
    grid-template-areas: "add remove-tags"
        "remaning .";
}

.tagify__dropdown.users-list .tagify__dropdown__item:hover .tagify__dropdown__item__avatar-wrap {
    transform: scale(1.2);
}

.tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap {
    grid-area: avatar;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    overflow: hidden;
    background: #EEE;
    transition: .1s ease-out;
}

.tagify__dropdown.users-list img {
    width: 100%;
    vertical-align: top;
}

.tagify__dropdown.users-list header.tagify__dropdown__item>div,
.tagify__dropdown.users-list .tagify__dropdown__item strong {
    grid-area: name;
    width: 100%;
    align-self: center;
}

.tagify__dropdown.users-list span {
    grid-area: email;
    width: 100%;
    font-size: .9em;
    opacity: .6;
}

.tagify__dropdown.users-list .tagify__dropdown__item__addAll {
    border-bottom: 1px solid #DDD;
    gap: 0;
}

.tagify__dropdown.users-list .remove-all-tags {
    grid-area: remove-tags;
    justify-self: self-end;
    font-size: .8em;
    padding: .2em .3em;
    border-radius: 3px;
    user-select: none;
}

.tagify__dropdown.users-list .remove-all-tags:hover {
    color: white;
    background: salmon;
}


/* Tags items */
.users-list .tagify__tag {
    white-space: nowrap;
}

.users-list .tagify__tag img {
    width: 100%;
    vertical-align: top;
    pointer-events: none;
}


.users-list .tagify__tag:hover .tagify__tag__avatar-wrap {
    transform: scale(1.6) translateX(-10%);
}

.users-list .tagify__tag .tagify__tag__avatar-wrap {
    width: 16px;
    height: 16px;
    white-space: normal;
    border-radius: 50%;
    background: silver;
    margin-right: 5px;
    transition: .12s ease-out;
}

.users-list .tagify__dropdown__itemsGroup:empty {
    display: none;
}

.users-list .tagify__dropdown__itemsGroup::before {
    content: attr(data-title);
    display: inline-block;
    font-size: .9em;
    padding: 4px 6px;
    margin: var(--tagify-dd-item-pad);
    font-style: italic;
    border-radius: 4px;
    background: #00ce8d;
    color: white;
    font-weight: 600;
}

.users-list .tagify__dropdown__itemsGroup:not(:first-of-type) {
    border-top: 1px solid #DDD;
}</style>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/qBZdOJe'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input name='users-list-tags' class='users-list' value='abatisse2@nih.gov, Justinian Hattersley'>
    </aside>

    <script>
        (function(){
        var inputElm = document.querySelector('input[name=users-list-tags]');

function tagTemplate(tagData) {
    return `
        <tag title="${tagData.email}"
                contenteditable='false'
                spellcheck='false'
                tabIndex="-1"
                class="tagify__tag ${tagData.class ? tagData.class : ""}"
                ${this.getAttributes(tagData)}>
            <x title='' class='tagify__tag__removeBtn' role='button' aria-label='remove tag'></x>
            <div>
                <div class='tagify__tag__avatar-wrap'>
                    <img onerror="this.style.visibility='hidden'" src="${tagData.avatar}">
                </div>
                <span class='tagify__tag-text'>${tagData.name}</span>
            </div>
        </tag>
    `
}

function suggestionItemTemplate(tagData) {
    return `
        <div ${this.getAttributes(tagData)}
            class='tagify__dropdown__item ${tagData.class ? tagData.class : ""}'
            tabindex="0"
            role="option">
            ${tagData.avatar ? `
                <div class='tagify__dropdown__item__avatar-wrap'>
                    <img onerror="this.style.visibility='hidden'" src="${tagData.avatar}">
                </div>` : ''
        }
            <strong>${tagData.name}</strong>
            <span>${tagData.email}</span>
        </div>
    `
}

function dropdownHeaderTemplate(suggestions) {
    return `
        <header data-selector='tagify-suggestions-header' class="${this.settings.classNames.dropdownItem} ${this.settings.classNames.dropdownItem}__addAll">
            <strong style='grid-area: add'>${this.value.length ? `Add Remaning` : 'Add All'}</strong>
            <span style='grid-area: remaning'>${suggestions.length} members</span>
            <a class='remove-all-tags'>Remove all</a>
        </header>
    `
}

// initialize Tagify on the above input node reference
var tagify = new Tagify(inputElm, {
    tagTextProp: 'name', // very important since a custom template is used with this property as text
    // enforceWhitelist: true,
    skipInvalid: true, // do not remporarily add invalid tags
    dropdown: {
        closeOnSelect: false,
        enabled: 0,
        classname: 'users-list',
        searchKeys: ['name', 'email']  // very important to set by which keys to search for suggesttions when typing
    },
    templates: {
        tag: tagTemplate,
        dropdownItem: suggestionItemTemplate,
        dropdownHeader: dropdownHeaderTemplate
    },
    whitelist: [
        {
            "value": 1,
            "name": "Justinian Hattersley",
            "avatar": "https://i.pravatar.cc/80?img=1",
            "email": "jhattersley0@ucsd.edu",
            "team": "A"
        },
        {
            "value": 2,
            "name": "Antons Esson",
            "avatar": "https://i.pravatar.cc/80?img=2",
            "email": "aesson1@ning.com",
            "team": "B"

        },
        {
            "value": 3,
            "name": "Ardeen Batisse",
            "avatar": "https://i.pravatar.cc/80?img=3",
            "email": "abatisse2@nih.gov",
            "team": "A"
        },
        {
            "value": 4,
            "name": "Graeme Yellowley",
            "avatar": "https://i.pravatar.cc/80?img=4",
            "email": "gyellowley3@behance.net",
            "team": "C"
        },
        {
            "value": 5,
            "name": "Dido Wilford",
            "avatar": "https://i.pravatar.cc/80?img=5",
            "email": "dwilford4@jugem.jp",
            "team": "A"
        },
        {
            "value": 6,
            "name": "Celesta Orwin",
            "avatar": "https://i.pravatar.cc/80?img=6",
            "email": "corwin5@meetup.com",
            "team": "C"
        },
        {
            "value": 7,
            "name": "Sally Main",
            "avatar": "https://i.pravatar.cc/80?img=7",
            "email": "smain6@techcrunch.com",
            "team": "A"
        },
        {
            "value": 8,
            "name": "Grethel Haysman",
            "avatar": "https://i.pravatar.cc/80?img=8",
            "email": "ghaysman7@mashable.com",
            "team": "B"
        },
        {
            "value": 9,
            "name": "Marvin Mandrake",
            "avatar": "https://i.pravatar.cc/80?img=9",
            "email": "mmandrake8@sourceforge.net",
            "team": "B"
        },
        {
            "value": 10,
            "name": "Corrie Tidey",
            "avatar": "https://i.pravatar.cc/80?img=10",
            "email": "ctidey9@youtube.com",
            "team": "A"
        },
        {
            "value": 11,
            "name": "foo",
            "avatar": "https://i.pravatar.cc/80?img=11",
            "email": "foo@bar.com",
            "team": "B"
        },
        {
            "value": 12,
            "name": "foo",
            "avatar": "https://i.pravatar.cc/80?img=12",
            "email": "foo.aaa@foo.com",
            "team": "A"
        },
    ],

    transformTag: (tagData, originalData) => {
        var { name, email } = parseFullValue(tagData.name)
        tagData.name = name
        tagData.email = email || tagData.email
    },

    validate({ name, email }) {
        // when editing a tag, there will only be the "name" property which contains name + email (see 'transformTag' above)
        if (!email && name) {
            var parsed = parseFullValue(name)
            name = parsed.name
            email = parsed.email
        }

        if (!name) return "Missing name"
        if (!validateEmail(email)) return "Invalid email"

        return true
    }
})

// The below code is printed as escaped, so please copy this function from:
// https://github.com/yairEO/tagify/blob/master/src/parts/helpers.js#L89-L97
function escapeHTML(s) {
    return typeof s == 'string' ? s
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/`|'/g, "&#039;")
        : s;
}

// The below part is only if you want to split the users into groups, when rendering the suggestions list dropdown:
// (since each user also has a 'team' property)
tagify.dropdown.createListHTML = sugegstionsList => {
    const teamsOfUsers = sugegstionsList.reduce((acc, suggestion) => {
        const team = suggestion.team || 'Not Assigned';

        if (!acc[team])
            acc[team] = [suggestion]
        else
            acc[team].push(suggestion)

        return acc
    }, {})

    const getUsersSuggestionsHTML = teamUsers => teamUsers.map((suggestion, idx) => {
        if (typeof suggestion == 'string' || typeof suggestion == 'number')
            suggestion = { value: suggestion }

        var value = tagify.dropdown.getMappedValue.call(tagify, suggestion)

        suggestion.value = value && typeof value == 'string' ? escapeHTML(value) : value

        return tagify.settings.templates.dropdownItem.apply(tagify, [suggestion]);
    }).join("")


    // assign the user to a group
    return Object.entries(teamsOfUsers).map(([team, teamUsers]) => {
        return `<div class="tagify__dropdown__itemsGroup" data-title="Team ${team}:">${getUsersSuggestionsHTML(teamUsers)}</div>`
    }).join("")
}

// attach events listeners
tagify.on('dropdown:select', onSelectSuggestion) // allows selecting all the suggested (whitelist) items
      .on('edit:start', onEditStart)  // show custom text in the tag while in edit-mode

function onSelectSuggestion(e) {
    if (e.detail.event.target.matches('.remove-all-tags')) {
        tagify.removeAllTags()
    }

    // custom class from "dropdownHeaderTemplate"
    else if (e.detail.elm.classList.contains(`${tagify.settings.classNames.dropdownItem}__addAll`))
        tagify.dropdown.selectAll();
}

function onEditStart({ detail: { tag, data } }) {
    tagify.setTagTextNode(tag, `${data.name} <${data.email}>`)
}

// https://stackoverflow.com/a/9204568/104380
function validateEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}

function parseFullValue(value) {
    // https://stackoverflow.com/a/11592042/104380
    var parts = value.split(/<(.*?)>/g),
        name = parts[0].trim(),
        email = parts[1]?.replace(/<(.*?)>/g, '').trim();

    return { name, email }
}
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-advance-options'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-advance-options'>advance-options</a></h2>
        </header>
        

        
                <p>
                    In this example, the <code>dropdown.enabled</code> <em>setting</em> is set (minimum charactes typed to show the dropdown) to <code>3</code>.
                    <br>
                    Maximum number of tags is set to <code>6</code>
                </p>
                <p>
                    Each (valid) tag gets a random color, via the <code>transformTag</code> callback which modifies the tag's data object before creating the tag element.
                    Another way of giving colors to tags is <a href='https://github.com/yairEO/tagify/issues/644'>discussed here</a>.
                    Clicking a tag <strong>once</strong> will change its color.
                </p>
                <p>
                    HTML5 <code>pattern</code> attribute is automatically used to validate tags.<br>
                    Also, the <code>delimiters</code> <em>setting</em> using both <code>comma</code> or <code>space</code> as tags seperators.
                </p>
                <p>
                    The <code>keepInvalidTags</code> <em>setting</em> flag is switched <em>on</em> to <code>true</code> so invaild tags are not removed but are only marked.
                </p>
                <p>
                    If there is no match for the typed text, a special dropdown item will be renderd using the optional <em>settings</em>: <code>templates.dropdownItemNoMatch</code>
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input class=&#39;advance-options&#39; value=&#39;[{&quot;value&quot;:&quot;point&quot;}, {&quot;value&quot;:&quot;soft&quot;}]&#39; pattern=&#39;^[A-Za-z_✲ ]{1,15}$&#39;&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input.advance-options'),
    tagify = new Tagify(input, {
        pattern             : /^.{0,20}$/,  // Validate typed tag(s) by Regex. Here maximum chars length is defined as "20"
        delimiters          : ",| ",        // add new tags when a comma or a space character is entered
        trim                : false,        // if "delimiters" setting is using space as a delimeter, then "trim" should be set to "false"
        keepInvalidTags     : true,         // do not remove invalid tags (but keep them marked as invalid)
        // createInvalidTags: false,
        editTags            : {
            clicks: 2,              // single click to edit a tag
            keepInvalid: false      // if after editing, tag is invalid, auto-revert
        },
        maxTags             : 6,
        blacklist           : ["foo", "bar", "baz"],
        whitelist           : ["temple","stun","detective","sign","passion","routine","deck","discriminate","relaxation","fraud","attractive","soft","forecast","point","thank","stage","eliminate","effective","flood","passive","skilled","separation","contact","compromise","reality","district","nationalist","leg","porter","conviction","worker","vegetable","commerce","conception","particle","honor","stick","tail","pumpkin","core","mouse","egg","population","unique","behavior","onion","disaster","cute","pipe","sock","dialect","horse","swear","owner","cope","global","improvement","artist","shed","constant","bond","brink","shower","spot","inject","bowel","homosexual","trust","exclude","tough","sickness","prevalence","sister","resolution","cattle","cultural","innocent","burial","bundle","thaw","respectable","thirsty","exposure","team","creed","facade","calendar","filter","utter","dominate","predator","discover","theorist","hospitality","damage","woman","rub","crop","unpleasant","halt","inch","birthday","lack","throne","maximum","pause","digress","fossil","policy","instrument","trunk","frame","measure","hall","support","convenience","house","partnership","inspector","looting","ranch","asset","rally","explicit","leak","monarch","ethics","applied","aviation","dentist","great","ethnic","sodium","truth","constellation","lease","guide","break","conclusion","button","recording","horizon","council","paradox","bride","weigh","like","noble","transition","accumulation","arrow","stitch","academy","glimpse","case","researcher","constitutional","notion","bathroom","revolutionary","soldier","vehicle","betray","gear","pan","quarter","embarrassment","golf","shark","constitution","club","college","duty","eaux","know","collection","burst","fun","animal","expectation","persist","insure","tick","account","initiative","tourist","member","example","plant","river","ratio","view","coast","latest","invite","help","falsify","allocation","degree","feel","resort","means","excuse","injury","pupil","shaft","allow","ton","tube","dress","speaker","double","theater","opposed","holiday","screw","cutting","picture","laborer","conservation","kneel","miracle","brand","nomination","characteristic","referral","carbon","valley","hot","climb","wrestle","motorist","update","loot","mosquito","delivery","eagle","guideline","hurt","feedback","finish","traffic","competence","serve","archive","feeling","hope","seal","ear","oven","vote","ballot","study","negative","declaration","particular","pattern","suburb","intervention","brake","frequency","drink","affair","contemporary","prince","dry","mole","lazy","undermine","radio","legislation","circumstance","bear","left","pony","industry","mastermind","criticism","sheep","failure","chain","depressed","launch","script","green","weave","please","surprise","doctor","revive","banquet","belong","correction","door","image","integrity","intermediate","sense","formal","cane","gloom","toast","pension","exception","prey","random","nose","predict","needle","satisfaction","establish","fit","vigorous","urgency","X-ray","equinox","variety","proclaim","conceive","bulb","vegetarian","available","stake","publicity","strikebreaker","portrait","sink","frog","ruin","studio","match","electron","captain","channel","navy","set","recommend","appoint","liberal","missile","sample","result","poor","efflux","glance","timetable","advertise","personality","aunt","dog"],
        transformTag        : transformTag,
        backspace           : "edit",
        placeholder         : "Type something",
        dropdown : {
            enabled: 1,            // show suggestion after 1 typed character
            fuzzySearch: false,    // match only suggestions that starts with the typed characters
            position: 'text',      // position suggestions list next to typed text
            caseSensitive: true,   // allow adding duplicate items if their case is different
        },
        templates: {
            dropdownItemNoMatch: function(data) {
                return `<div class='${this.settings.classNames.dropdownItem}' value="noMatch" tabindex="0" role="option">No suggestion found for: <strong>${data.value}</strong></div>`
            }
        }
    })

tagify.on('change', updatePlaceholderByTagsCount);

function updatePlaceholderByTagsCount() {
    tagify.setPlaceholder(`${tagify.value.length || 'no'} tags added`)
}

updatePlaceholderByTagsCount()

// generate a random color (in HSL format, which I like to use)
function getRandomColor(){
    function rand(min, max) {
        return min + Math.random() * (max - min);
    }

    var h = rand(1, 360)|0,
        s = rand(40, 70)|0,
        l = rand(65, 72)|0;

    return 'hsl(' + h + ',' + s + '%,' + l + '%)';
}

function transformTag( tagData ){
    tagData.color = getRandomColor();
    tagData.style = "--tag-bg:" + tagData.color;

    if( tagData.value.toLowerCase() == 'shit' )
        tagData.value = 's✲✲t'
}

tagify.on('add', function(e){
    console.log(e.detail)
})

tagify.on('invalid', function(e){
    console.log(e, e.detail);
})

var clickDebounce;

tagify.on('click', function(e){
    const {tag:tagElm, data:tagData} = e.detail;

    // a delay is needed to distinguish between regular click and double-click.
    // this allows enough time for a possible double-click, and noly fires if such
    // did not occur.
    clearTimeout(clickDebounce);
    clickDebounce = setTimeout(() => {
        tagData.color = getRandomColor();
        tagData.style = "--tag-bg:" + tagData.color;
        tagify.replaceTag(tagElm, tagData);
    }, 200);
})

tagify.on('dblclick', function(e){
    // when souble clicking, do not change the color of the tag
    clearTimeout(clickDebounce);
})</code></pre>
            
            <h3>CSS</h3>
            <style contenteditable>.advance-options .tagify__tag{
    --tag-hover: var(--tag-bg);
}</style>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/bGpdVOr'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input class='advance-options' value='[{"value":"point"}, {"value":"soft"}]' pattern='^[A-Za-z_✲ ]{1,15}$'>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input.advance-options'),
    tagify = new Tagify(input, {
        pattern             : /^.{0,20}$/,  // Validate typed tag(s) by Regex. Here maximum chars length is defined as "20"
        delimiters          : ",| ",        // add new tags when a comma or a space character is entered
        trim                : false,        // if "delimiters" setting is using space as a delimeter, then "trim" should be set to "false"
        keepInvalidTags     : true,         // do not remove invalid tags (but keep them marked as invalid)
        // createInvalidTags: false,
        editTags            : {
            clicks: 2,              // single click to edit a tag
            keepInvalid: false      // if after editing, tag is invalid, auto-revert
        },
        maxTags             : 6,
        blacklist           : ["foo", "bar", "baz"],
        whitelist           : ["temple","stun","detective","sign","passion","routine","deck","discriminate","relaxation","fraud","attractive","soft","forecast","point","thank","stage","eliminate","effective","flood","passive","skilled","separation","contact","compromise","reality","district","nationalist","leg","porter","conviction","worker","vegetable","commerce","conception","particle","honor","stick","tail","pumpkin","core","mouse","egg","population","unique","behavior","onion","disaster","cute","pipe","sock","dialect","horse","swear","owner","cope","global","improvement","artist","shed","constant","bond","brink","shower","spot","inject","bowel","homosexual","trust","exclude","tough","sickness","prevalence","sister","resolution","cattle","cultural","innocent","burial","bundle","thaw","respectable","thirsty","exposure","team","creed","facade","calendar","filter","utter","dominate","predator","discover","theorist","hospitality","damage","woman","rub","crop","unpleasant","halt","inch","birthday","lack","throne","maximum","pause","digress","fossil","policy","instrument","trunk","frame","measure","hall","support","convenience","house","partnership","inspector","looting","ranch","asset","rally","explicit","leak","monarch","ethics","applied","aviation","dentist","great","ethnic","sodium","truth","constellation","lease","guide","break","conclusion","button","recording","horizon","council","paradox","bride","weigh","like","noble","transition","accumulation","arrow","stitch","academy","glimpse","case","researcher","constitutional","notion","bathroom","revolutionary","soldier","vehicle","betray","gear","pan","quarter","embarrassment","golf","shark","constitution","club","college","duty","eaux","know","collection","burst","fun","animal","expectation","persist","insure","tick","account","initiative","tourist","member","example","plant","river","ratio","view","coast","latest","invite","help","falsify","allocation","degree","feel","resort","means","excuse","injury","pupil","shaft","allow","ton","tube","dress","speaker","double","theater","opposed","holiday","screw","cutting","picture","laborer","conservation","kneel","miracle","brand","nomination","characteristic","referral","carbon","valley","hot","climb","wrestle","motorist","update","loot","mosquito","delivery","eagle","guideline","hurt","feedback","finish","traffic","competence","serve","archive","feeling","hope","seal","ear","oven","vote","ballot","study","negative","declaration","particular","pattern","suburb","intervention","brake","frequency","drink","affair","contemporary","prince","dry","mole","lazy","undermine","radio","legislation","circumstance","bear","left","pony","industry","mastermind","criticism","sheep","failure","chain","depressed","launch","script","green","weave","please","surprise","doctor","revive","banquet","belong","correction","door","image","integrity","intermediate","sense","formal","cane","gloom","toast","pension","exception","prey","random","nose","predict","needle","satisfaction","establish","fit","vigorous","urgency","X-ray","equinox","variety","proclaim","conceive","bulb","vegetarian","available","stake","publicity","strikebreaker","portrait","sink","frog","ruin","studio","match","electron","captain","channel","navy","set","recommend","appoint","liberal","missile","sample","result","poor","efflux","glance","timetable","advertise","personality","aunt","dog"],
        transformTag        : transformTag,
        backspace           : "edit",
        placeholder         : "Type something",
        dropdown : {
            enabled: 1,            // show suggestion after 1 typed character
            fuzzySearch: false,    // match only suggestions that starts with the typed characters
            position: 'text',      // position suggestions list next to typed text
            caseSensitive: true,   // allow adding duplicate items if their case is different
        },
        templates: {
            dropdownItemNoMatch: function(data) {
                return `<div class='${this.settings.classNames.dropdownItem}' value="noMatch" tabindex="0" role="option">No suggestion found for: <strong>${data.value}</strong></div>`
            }
        }
    })

tagify.on('change', updatePlaceholderByTagsCount);

function updatePlaceholderByTagsCount() {
    tagify.setPlaceholder(`${tagify.value.length || 'no'} tags added`)
}

updatePlaceholderByTagsCount()

// generate a random color (in HSL format, which I like to use)
function getRandomColor(){
    function rand(min, max) {
        return min + Math.random() * (max - min);
    }

    var h = rand(1, 360)|0,
        s = rand(40, 70)|0,
        l = rand(65, 72)|0;

    return 'hsl(' + h + ',' + s + '%,' + l + '%)';
}

function transformTag( tagData ){
    tagData.color = getRandomColor();
    tagData.style = "--tag-bg:" + tagData.color;

    if( tagData.value.toLowerCase() == 'shit' )
        tagData.value = 's✲✲t'
}

tagify.on('add', function(e){
    console.log(e.detail)
})

tagify.on('invalid', function(e){
    console.log(e, e.detail);
})

var clickDebounce;

tagify.on('click', function(e){
    const {tag:tagElm, data:tagData} = e.detail;

    // a delay is needed to distinguish between regular click and double-click.
    // this allows enough time for a possible double-click, and noly fires if such
    // did not occur.
    clearTimeout(clickDebounce);
    clickDebounce = setTimeout(() => {
        tagData.color = getRandomColor();
        tagData.style = "--tag-bg:" + tagData.color;
        tagify.replaceTag(tagElm, tagData);
    }, 200);
})

tagify.on('dblclick', function(e){
    // when souble clicking, do not change the color of the tag
    clearTimeout(clickDebounce);
})
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-extra-properties'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-extra-properties'>Tags with properties</a></h2>
        </header>
        

        
                <p>
                    Some cases requires more control per-tag, for example, sending a different value to the server than the textual value
                    the user sees/entered.
                    <br/><br/>
                    Another example, would be different colors for different tags or tags' groups.
                    <br/><br/>
                    It's possible to add any number of properties for a tag. The only constant is the <code>value</code> property which
                    must be unique and declared per-tag, and by default that will be the rendered text, unless specified otherwise using the settings.
                </p>
                <p>
                    The properties shown in the example below, declared in the <code>allowedTags</code> Array, will be transformed into
                    HTML attributes for each tag element rendered from the allowed <code>whitelist</code> settings, so naturally
                    some attributes are in the specs and the made-up ones should technically be prefixed with <code>data-</code>.
                </p>
                <p>
                    💡 In this example, some countries have an extra property <code>searchBy</code> which is used for smarter suggestions matching when the user types something.
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input class=&#39;countries&#39; name=&#39;extra-properties&#39; placeholder=&quot;Try adding tags from the list&quot;&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var tagify = new Tagify(document.querySelector('input[name=extra-properties]'), {
    delimiters: null,
    templates: {
        tag: function (tagData) {
            try {
                return `<tag title='${tagData.value}' contenteditable='false' spellcheck="false" class='tagify__tag ${tagData.class ? tagData.class : ""}' ${this.getAttributes(tagData)}>
                        <x title='remove tag' class='tagify__tag__removeBtn'></x>
                        <div>
                            ${tagData.code ?
                        `<img onerror="this.style.visibility='hidden'" src='https://flagicons.lipis.dev/flags/4x3/${tagData.code.toLowerCase()}.svg'>` : ''
                    }
                            <span class='tagify__tag-text'>${tagData.value}</span>
                        </div>
                    </tag>`
            }
            catch (err) { }
        },

        dropdownItem: function (tagData) {
            try {
                return `<div ${this.getAttributes(tagData)} class='tagify__dropdown__item ${tagData.class ? tagData.class : ""}' >
                            <img onerror="this.style.visibility = 'hidden'"
                                src='https://flagicons.lipis.dev/flags/4x3/${tagData.code.toLowerCase()}.svg'>
                            <span>${tagData.value}</span>
                        </div>`
            }
            catch (err) { console.error(err) }
        }
    },
    enforceWhitelist: true,
    whitelist: [
        { value: 'Afghanistan', code: 'AF' },
        { value: 'Åland Islands', code: 'AX' },
        { value: 'Albania', code: 'AL' },
        { value: 'Algeria', code: 'DZ' },
        { value: 'American Samoa', code: 'AS' },
        { value: 'Andorra', code: 'AD' },
        { value: 'Angola', code: 'AO' },
        { value: 'Anguilla', code: 'AI' },
        { value: 'Antarctica', code: 'AQ' },
        { value: 'Antigua and Barbuda', code: 'AG' },
        { value: 'Argentina', code: 'AR' },
        { value: 'Armenia', code: 'AM' },
        { value: 'Aruba', code: 'AW' },
        { value: 'Australia', code: 'AU', searchBy: 'beach, sub-tropical' },
        { value: 'Austria', code: 'AT' },
        { value: 'Azerbaijan', code: 'AZ' },
        { value: 'Bahamas', code: 'BS' },
        { value: 'Bahrain', code: 'BH' },
        { value: 'Bangladesh', code: 'BD' },
        { value: 'Barbados', code: 'BB' },
        { value: 'Belarus', code: 'BY' },
        { value: 'Belgium', code: 'BE' },
        { value: 'Belize', code: 'BZ' },
        { value: 'Benin', code: 'BJ' },
        { value: 'Bermuda', code: 'BM' },
        { value: 'Bhutan', code: 'BT' },
        { value: 'Bolivia', code: 'BO' },
        { value: 'Bosnia and Herzegovina', code: 'BA' },
        { value: 'Botswana', code: 'BW' },
        { value: 'Bouvet Island', code: 'BV' },
        { value: 'Brazil', code: 'BR' },
        { value: 'British Indian Ocean Territory', code: 'IO' },
        { value: 'Brunei Darussalam', code: 'BN' },
        { value: 'Bulgaria', code: 'BG' },
        { value: 'Burkina Faso', code: 'BF' },
        { value: 'Burundi', code: 'BI' },
        { value: 'Cambodia', code: 'KH' },
        { value: 'Cameroon', code: 'CM' },
        { value: 'Canada', code: 'CA' },
        { value: 'Cape Verde', code: 'CV' },
        { value: 'Cayman Islands', code: 'KY' },
        { value: 'Central African Republic', code: 'CF' },
        { value: 'Chad', code: 'TD' },
        { value: 'Chile', code: 'CL' },
        { value: 'China', code: 'CN' },
        { value: 'Christmas Island', code: 'CX' },
        { value: 'Cocos (Keeling) Islands', code: 'CC' },
        { value: 'Colombia', code: 'CO' },
        { value: 'Comoros', code: 'KM' },
        { value: 'Congo', code: 'CG' },
        { value: 'Congo, The Democratic Republic of the', code: 'CD' },
        { value: 'Cook Islands', code: 'CK' },
        { value: 'Costa Rica', code: 'CR' },
        { value: 'Cote D\'Ivoire', code: 'CI' },
        { value: 'Croatia', code: 'HR' },
        { value: 'Cuba', code: 'CU' },
        { value: 'Cyprus', code: 'CY' },
        { value: 'Czech Republic', code: 'CZ' },
        { value: 'Denmark', code: 'DK' },
        { value: 'Djibouti', code: 'DJ' },
        { value: 'Dominica', code: 'DM' },
        { value: 'Dominican Republic', code: 'DO' },
        { value: 'Ecuador', code: 'EC' },
        { value: 'Egypt', code: 'EG' },
        { value: 'El Salvador', code: 'SV' },
        { value: 'Equatorial Guinea', code: 'GQ' },
        { value: 'Eritrea', code: 'ER' },
        { value: 'Estonia', code: 'EE' },
        { value: 'Ethiopia', code: 'ET' },
        { value: 'Falkland Islands (Malvinas)', code: 'FK' },
        { value: 'Faroe Islands', code: 'FO' },
        { value: 'Fiji', code: 'FJ' },
        { value: 'Finland', code: 'FI' },
        { value: 'France', code: 'FR' },
        { value: 'French Guiana', code: 'GF' },
        { value: 'French Polynesia', code: 'PF' },
        { value: 'French Southern Territories', code: 'TF' },
        { value: 'Gabon', code: 'GA' },
        { value: 'Gambia', code: 'GM' },
        { value: 'Georgia', code: 'GE' },
        { value: 'Germany', code: 'DE' },
        { value: 'Ghana', code: 'GH' },
        { value: 'Gibraltar', code: 'GI' },
        { value: 'Greece', code: 'GR' },
        { value: 'Greenland', code: 'GL' },
        { value: 'Grenada', code: 'GD' },
        { value: 'Guadeloupe', code: 'GP' },
        { value: 'Guam', code: 'GU' },
        { value: 'Guatemala', code: 'GT' },
        { value: 'Guernsey', code: 'GG' },
        { value: 'Guinea', code: 'GN' },
        { value: 'Guinea-Bissau', code: 'GW' },
        { value: 'Guyana', code: 'GY' },
        { value: 'Haiti', code: 'HT' },
        { value: 'Heard Island and Mcdonald Islands', code: 'HM' },
        { value: 'Holy See (Vatican City State)', code: 'VA' },
        { value: 'Honduras', code: 'HN' },
        { value: 'Hong Kong', code: 'HK' },
        { value: 'Hungary', code: 'HU' },
        { value: 'Iceland', code: 'IS' },
        { value: 'India', code: 'IN' },
        { value: 'Indonesia', code: 'ID' },
        { value: 'Iran, Islamic Republic Of', code: 'IR' },
        { value: 'Iraq', code: 'IQ' },
        { value: 'Ireland', code: 'IE' },
        { value: 'Isle of Man', code: 'IM' },
        { value: 'Israel', code: 'IL', searchBy: 'holy land, desert' },
        { value: 'Italy', code: 'IT' },
        { value: 'Jamaica', code: 'JM' },
        { value: 'Japan', code: 'JP' },
        { value: 'Jersey', code: 'JE' },
        { value: 'Jordan', code: 'JO' },
        { value: 'Kazakhstan', code: 'KZ' },
        { value: 'Kenya', code: 'KE' },
        { value: 'Kiribati', code: 'KI' },
        { value: 'Korea, Democratic People\'S Republic of', code: 'KP' },
        { value: 'Korea, Republic of', code: 'KR' },
        { value: 'Kuwait', code: 'KW' },
        { value: 'Kyrgyzstan', code: 'KG' },
        { value: 'Lao People\'S Democratic Republic', code: 'LA' },
        { value: 'Latvia', code: 'LV' },
        { value: 'Lebanon', code: 'LB' },
        { value: 'Lesotho', code: 'LS' },
        { value: 'Liberia', code: 'LR' },
        { value: 'Libyan Arab Jamahiriya', code: 'LY' },
        { value: 'Liechtenstein', code: 'LI' },
        { value: 'Lithuania', code: 'LT' },
        { value: 'Luxembourg', code: 'LU' },
        { value: 'Macao', code: 'MO' },
        { value: 'Macedonia, The Former Yugoslav Republic of', code: 'MK' },
        { value: 'Madagascar', code: 'MG' },
        { value: 'Malawi', code: 'MW' },
        { value: 'Malaysia', code: 'MY' },
        { value: 'Maldives', code: 'MV' },
        { value: 'Mali', code: 'ML' },
        { value: 'Malta', code: 'MT' },
        { value: 'Marshall Islands', code: 'MH' },
        { value: 'Martinique', code: 'MQ' },
        { value: 'Mauritania', code: 'MR' },
        { value: 'Mauritius', code: 'MU' },
        { value: 'Mayotte', code: 'YT' },
        { value: 'Mexico', code: 'MX' },
        { value: 'Micronesia, Federated States of', code: 'FM' },
        { value: 'Moldova, Republic of', code: 'MD' },
        { value: 'Monaco', code: 'MC' },
        { value: 'Mongolia', code: 'MN' },
        { value: 'Montserrat', code: 'MS' },
        { value: 'Morocco', code: 'MA' },
        { value: 'Mozambique', code: 'MZ' },
        { value: 'Myanmar', code: 'MM' },
        { value: 'Namibia', code: 'NA' },
        { value: 'Nauru', code: 'NR' },
        { value: 'Nepal', code: 'NP' },
        { value: 'Netherlands', code: 'NL' },
        { value: 'Netherlands Antilles', code: 'AN' },
        { value: 'New Caledonia', code: 'NC' },
        { value: 'New Zealand', code: 'NZ' },
        { value: 'Nicaragua', code: 'NI' },
        { value: 'Niger', code: 'NE' },
        { value: 'Nigeria', code: 'NG' },
        { value: 'Niue', code: 'NU' },
        { value: 'Norfolk Island', code: 'NF' },
        { value: 'Northern Mariana Islands', code: 'MP' },
        { value: 'Norway', code: 'NO' },
        { value: 'Oman', code: 'OM' },
        { value: 'Pakistan', code: 'PK' },
        { value: 'Palau', code: 'PW' },
        { value: 'Palestinian Territory, Occupied', code: 'PS' },
        { value: 'Panama', code: 'PA' },
        { value: 'Papua New Guinea', code: 'PG' },
        { value: 'Paraguay', code: 'PY' },
        { value: 'Peru', code: 'PE' },
        { value: 'Philippines', code: 'PH' },
        { value: 'Pitcairn', code: 'PN' },
        { value: 'Poland', code: 'PL' },
        { value: 'Portugal', code: 'PT' },
        { value: 'Puerto Rico', code: 'PR' },
        { value: 'Qatar', code: 'QA' },
        { value: 'Reunion', code: 'RE' },
        { value: 'Romania', code: 'RO' },
        { value: 'Russian Federation', code: 'RU' },
        { value: 'RWANDA', code: 'RW' },
        { value: 'Saint Helena', code: 'SH' },
        { value: 'Saint Kitts and Nevis', code: 'KN' },
        { value: 'Saint Lucia', code: 'LC' },
        { value: 'Saint Pierre and Miquelon', code: 'PM' },
        { value: 'Saint Vincent and the Grenadines', code: 'VC' },
        { value: 'Samoa', code: 'WS' },
        { value: 'San Marino', code: 'SM' },
        { value: 'Sao Tome and Principe', code: 'ST' },
        { value: 'Saudi Arabia', code: 'SA' },
        { value: 'Senegal', code: 'SN' },
        { value: 'Serbia and Montenegro', code: 'CS' },
        { value: 'Seychelles', code: 'SC' },
        { value: 'Sierra Leone', code: 'SL' },
        { value: 'Singapore', code: 'SG' },
        { value: 'Slovakia', code: 'SK' },
        { value: 'Slovenia', code: 'SI' },
        { value: 'Solomon Islands', code: 'SB' },
        { value: 'Somalia', code: 'SO' },
        { value: 'South Africa', code: 'ZA' },
        { value: 'South Georgia and the South Sandwich Islands', code: 'GS' },
        { value: 'Spain', code: 'ES' },
        { value: 'Sri Lanka', code: 'LK' },
        { value: 'Sudan', code: 'SD' },
        { value: 'Suriname', code: 'SR' },
        { value: 'Svalbard and Jan Mayen', code: 'SJ' },
        { value: 'Swaziland', code: 'SZ' },
        { value: 'Sweden', code: 'SE' },
        { value: 'Switzerland', code: 'CH' },
        { value: 'Syrian Arab Republic', code: 'SY' },
        { value: 'Taiwan', code: 'TW' },
        { value: 'Tajikistan', code: 'TJ' },
        { value: 'Tanzania, United Republic of', code: 'TZ' },
        { value: 'Thailand', code: 'TH' },
        { value: 'Timor-Leste', code: 'TL' },
        { value: 'Togo', code: 'TG' },
        { value: 'Tokelau', code: 'TK' },
        { value: 'Tonga', code: 'TO' },
        { value: 'Trinidad and Tobago', code: 'TT' },
        { value: 'Tunisia', code: 'TN' },
        { value: 'Turkey', code: 'TR' },
        { value: 'Turkmenistan', code: 'TM' },
        { value: 'Turks and Caicos Islands', code: 'TC' },
        { value: 'Tuvalu', code: 'TV' },
        { value: 'Uganda', code: 'UG' },
        { value: 'Ukraine', code: 'UA' },
        { value: 'United Arab Emirates', code: 'AE' },
        { value: 'United Kingdom', code: 'GB' },
        { value: 'United States', code: 'US' },
        { value: 'United States Minor Outlying Islands', code: 'UM' },
        { value: 'Uruguay', code: 'UY' },
        { value: 'Uzbekistan', code: 'UZ' },
        { value: 'Vanuatu', code: 'VU' },
        { value: 'Venezuela', code: 'VE' },
        { value: 'Viet Nam', code: 'VN' },
        { value: 'Virgin Islands, British', code: 'VG' },
        { value: 'Virgin Islands, U.S.', code: 'VI' },
        { value: 'Wallis and Futuna', code: 'WF' },
        { value: 'Western Sahara', code: 'EH' },
        { value: 'Yemen', code: 'YE' },
        { value: 'Zambia', code: 'ZM' },
        { value: 'Zimbabwe', code: 'ZW' }
    ],
    dropdown: {
        enabled: 1, // suggest tags after a single character input
        classname: 'extra-properties' // custom class for the suggestions dropdown
    } // map tags' values to this property name, so this property will be the actual value and not the printed value on the screen
})

tagify.on('click', function (e) {
    console.log(e.detail);
});

tagify.on('remove', function (e) {
    console.log(e.detail);
});

tagify.on('add', function (e) {
    console.log("original Input:", tagify.DOM.originalInput);
    console.log("original Input's value:", tagify.DOM.originalInput.value);
    console.log("event detail:", e.detail);
});

// add the first 2 tags and makes them readonly
var tagsToAdd = tagify.whitelist.slice(0, 2)
tagify.addTags(tagsToAdd)</code></pre>
            
            <h3>CSS</h3>
            <style contenteditable>.tagify__dropdown.extra-properties .tagify__dropdown__item > img{
    display: inline-block;
    vertical-align: middle;
    height: 20px;
    transform: scale(.75);
    margin-right: 5px;
    border-radius: 2px;
    transition: .12s ease-out;
}

.tagify__dropdown.extra-properties .tagify__dropdown__item--active > img,
.tagify__dropdown.extra-properties .tagify__dropdown__item:hover > img{
    transform: none;
    margin-right: 12px;
}

.tagify.countries .tagify__input{ min-width:175px; }

.tagify.countries tag{ white-space:nowrap; }
.tagify.countries tag img{
    display: inline-block;
    height: 16px;
    margin-right: 3px;
    border-radius: 2px;
    pointer-events: none;
}</style>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/ExKjVJJ'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input class='countries' name='extra-properties' placeholder="Try adding tags from the list">
    </aside>

    <script>
        (function(){
        var tagify = new Tagify(document.querySelector('input[name=extra-properties]'), {
    delimiters: null,
    templates: {
        tag: function (tagData) {
            try {
                return `<tag title='${tagData.value}' contenteditable='false' spellcheck="false" class='tagify__tag ${tagData.class ? tagData.class : ""}' ${this.getAttributes(tagData)}>
                        <x title='remove tag' class='tagify__tag__removeBtn'></x>
                        <div>
                            ${tagData.code ?
                        `<img onerror="this.style.visibility='hidden'" src='https://flagicons.lipis.dev/flags/4x3/${tagData.code.toLowerCase()}.svg'>` : ''
                    }
                            <span class='tagify__tag-text'>${tagData.value}</span>
                        </div>
                    </tag>`
            }
            catch (err) { }
        },

        dropdownItem: function (tagData) {
            try {
                return `<div ${this.getAttributes(tagData)} class='tagify__dropdown__item ${tagData.class ? tagData.class : ""}' >
                            <img onerror="this.style.visibility = 'hidden'"
                                src='https://flagicons.lipis.dev/flags/4x3/${tagData.code.toLowerCase()}.svg'>
                            <span>${tagData.value}</span>
                        </div>`
            }
            catch (err) { console.error(err) }
        }
    },
    enforceWhitelist: true,
    whitelist: [
        { value: 'Afghanistan', code: 'AF' },
        { value: 'Åland Islands', code: 'AX' },
        { value: 'Albania', code: 'AL' },
        { value: 'Algeria', code: 'DZ' },
        { value: 'American Samoa', code: 'AS' },
        { value: 'Andorra', code: 'AD' },
        { value: 'Angola', code: 'AO' },
        { value: 'Anguilla', code: 'AI' },
        { value: 'Antarctica', code: 'AQ' },
        { value: 'Antigua and Barbuda', code: 'AG' },
        { value: 'Argentina', code: 'AR' },
        { value: 'Armenia', code: 'AM' },
        { value: 'Aruba', code: 'AW' },
        { value: 'Australia', code: 'AU', searchBy: 'beach, sub-tropical' },
        { value: 'Austria', code: 'AT' },
        { value: 'Azerbaijan', code: 'AZ' },
        { value: 'Bahamas', code: 'BS' },
        { value: 'Bahrain', code: 'BH' },
        { value: 'Bangladesh', code: 'BD' },
        { value: 'Barbados', code: 'BB' },
        { value: 'Belarus', code: 'BY' },
        { value: 'Belgium', code: 'BE' },
        { value: 'Belize', code: 'BZ' },
        { value: 'Benin', code: 'BJ' },
        { value: 'Bermuda', code: 'BM' },
        { value: 'Bhutan', code: 'BT' },
        { value: 'Bolivia', code: 'BO' },
        { value: 'Bosnia and Herzegovina', code: 'BA' },
        { value: 'Botswana', code: 'BW' },
        { value: 'Bouvet Island', code: 'BV' },
        { value: 'Brazil', code: 'BR' },
        { value: 'British Indian Ocean Territory', code: 'IO' },
        { value: 'Brunei Darussalam', code: 'BN' },
        { value: 'Bulgaria', code: 'BG' },
        { value: 'Burkina Faso', code: 'BF' },
        { value: 'Burundi', code: 'BI' },
        { value: 'Cambodia', code: 'KH' },
        { value: 'Cameroon', code: 'CM' },
        { value: 'Canada', code: 'CA' },
        { value: 'Cape Verde', code: 'CV' },
        { value: 'Cayman Islands', code: 'KY' },
        { value: 'Central African Republic', code: 'CF' },
        { value: 'Chad', code: 'TD' },
        { value: 'Chile', code: 'CL' },
        { value: 'China', code: 'CN' },
        { value: 'Christmas Island', code: 'CX' },
        { value: 'Cocos (Keeling) Islands', code: 'CC' },
        { value: 'Colombia', code: 'CO' },
        { value: 'Comoros', code: 'KM' },
        { value: 'Congo', code: 'CG' },
        { value: 'Congo, The Democratic Republic of the', code: 'CD' },
        { value: 'Cook Islands', code: 'CK' },
        { value: 'Costa Rica', code: 'CR' },
        { value: 'Cote D\'Ivoire', code: 'CI' },
        { value: 'Croatia', code: 'HR' },
        { value: 'Cuba', code: 'CU' },
        { value: 'Cyprus', code: 'CY' },
        { value: 'Czech Republic', code: 'CZ' },
        { value: 'Denmark', code: 'DK' },
        { value: 'Djibouti', code: 'DJ' },
        { value: 'Dominica', code: 'DM' },
        { value: 'Dominican Republic', code: 'DO' },
        { value: 'Ecuador', code: 'EC' },
        { value: 'Egypt', code: 'EG' },
        { value: 'El Salvador', code: 'SV' },
        { value: 'Equatorial Guinea', code: 'GQ' },
        { value: 'Eritrea', code: 'ER' },
        { value: 'Estonia', code: 'EE' },
        { value: 'Ethiopia', code: 'ET' },
        { value: 'Falkland Islands (Malvinas)', code: 'FK' },
        { value: 'Faroe Islands', code: 'FO' },
        { value: 'Fiji', code: 'FJ' },
        { value: 'Finland', code: 'FI' },
        { value: 'France', code: 'FR' },
        { value: 'French Guiana', code: 'GF' },
        { value: 'French Polynesia', code: 'PF' },
        { value: 'French Southern Territories', code: 'TF' },
        { value: 'Gabon', code: 'GA' },
        { value: 'Gambia', code: 'GM' },
        { value: 'Georgia', code: 'GE' },
        { value: 'Germany', code: 'DE' },
        { value: 'Ghana', code: 'GH' },
        { value: 'Gibraltar', code: 'GI' },
        { value: 'Greece', code: 'GR' },
        { value: 'Greenland', code: 'GL' },
        { value: 'Grenada', code: 'GD' },
        { value: 'Guadeloupe', code: 'GP' },
        { value: 'Guam', code: 'GU' },
        { value: 'Guatemala', code: 'GT' },
        { value: 'Guernsey', code: 'GG' },
        { value: 'Guinea', code: 'GN' },
        { value: 'Guinea-Bissau', code: 'GW' },
        { value: 'Guyana', code: 'GY' },
        { value: 'Haiti', code: 'HT' },
        { value: 'Heard Island and Mcdonald Islands', code: 'HM' },
        { value: 'Holy See (Vatican City State)', code: 'VA' },
        { value: 'Honduras', code: 'HN' },
        { value: 'Hong Kong', code: 'HK' },
        { value: 'Hungary', code: 'HU' },
        { value: 'Iceland', code: 'IS' },
        { value: 'India', code: 'IN' },
        { value: 'Indonesia', code: 'ID' },
        { value: 'Iran, Islamic Republic Of', code: 'IR' },
        { value: 'Iraq', code: 'IQ' },
        { value: 'Ireland', code: 'IE' },
        { value: 'Isle of Man', code: 'IM' },
        { value: 'Israel', code: 'IL', searchBy: 'holy land, desert' },
        { value: 'Italy', code: 'IT' },
        { value: 'Jamaica', code: 'JM' },
        { value: 'Japan', code: 'JP' },
        { value: 'Jersey', code: 'JE' },
        { value: 'Jordan', code: 'JO' },
        { value: 'Kazakhstan', code: 'KZ' },
        { value: 'Kenya', code: 'KE' },
        { value: 'Kiribati', code: 'KI' },
        { value: 'Korea, Democratic People\'S Republic of', code: 'KP' },
        { value: 'Korea, Republic of', code: 'KR' },
        { value: 'Kuwait', code: 'KW' },
        { value: 'Kyrgyzstan', code: 'KG' },
        { value: 'Lao People\'S Democratic Republic', code: 'LA' },
        { value: 'Latvia', code: 'LV' },
        { value: 'Lebanon', code: 'LB' },
        { value: 'Lesotho', code: 'LS' },
        { value: 'Liberia', code: 'LR' },
        { value: 'Libyan Arab Jamahiriya', code: 'LY' },
        { value: 'Liechtenstein', code: 'LI' },
        { value: 'Lithuania', code: 'LT' },
        { value: 'Luxembourg', code: 'LU' },
        { value: 'Macao', code: 'MO' },
        { value: 'Macedonia, The Former Yugoslav Republic of', code: 'MK' },
        { value: 'Madagascar', code: 'MG' },
        { value: 'Malawi', code: 'MW' },
        { value: 'Malaysia', code: 'MY' },
        { value: 'Maldives', code: 'MV' },
        { value: 'Mali', code: 'ML' },
        { value: 'Malta', code: 'MT' },
        { value: 'Marshall Islands', code: 'MH' },
        { value: 'Martinique', code: 'MQ' },
        { value: 'Mauritania', code: 'MR' },
        { value: 'Mauritius', code: 'MU' },
        { value: 'Mayotte', code: 'YT' },
        { value: 'Mexico', code: 'MX' },
        { value: 'Micronesia, Federated States of', code: 'FM' },
        { value: 'Moldova, Republic of', code: 'MD' },
        { value: 'Monaco', code: 'MC' },
        { value: 'Mongolia', code: 'MN' },
        { value: 'Montserrat', code: 'MS' },
        { value: 'Morocco', code: 'MA' },
        { value: 'Mozambique', code: 'MZ' },
        { value: 'Myanmar', code: 'MM' },
        { value: 'Namibia', code: 'NA' },
        { value: 'Nauru', code: 'NR' },
        { value: 'Nepal', code: 'NP' },
        { value: 'Netherlands', code: 'NL' },
        { value: 'Netherlands Antilles', code: 'AN' },
        { value: 'New Caledonia', code: 'NC' },
        { value: 'New Zealand', code: 'NZ' },
        { value: 'Nicaragua', code: 'NI' },
        { value: 'Niger', code: 'NE' },
        { value: 'Nigeria', code: 'NG' },
        { value: 'Niue', code: 'NU' },
        { value: 'Norfolk Island', code: 'NF' },
        { value: 'Northern Mariana Islands', code: 'MP' },
        { value: 'Norway', code: 'NO' },
        { value: 'Oman', code: 'OM' },
        { value: 'Pakistan', code: 'PK' },
        { value: 'Palau', code: 'PW' },
        { value: 'Palestinian Territory, Occupied', code: 'PS' },
        { value: 'Panama', code: 'PA' },
        { value: 'Papua New Guinea', code: 'PG' },
        { value: 'Paraguay', code: 'PY' },
        { value: 'Peru', code: 'PE' },
        { value: 'Philippines', code: 'PH' },
        { value: 'Pitcairn', code: 'PN' },
        { value: 'Poland', code: 'PL' },
        { value: 'Portugal', code: 'PT' },
        { value: 'Puerto Rico', code: 'PR' },
        { value: 'Qatar', code: 'QA' },
        { value: 'Reunion', code: 'RE' },
        { value: 'Romania', code: 'RO' },
        { value: 'Russian Federation', code: 'RU' },
        { value: 'RWANDA', code: 'RW' },
        { value: 'Saint Helena', code: 'SH' },
        { value: 'Saint Kitts and Nevis', code: 'KN' },
        { value: 'Saint Lucia', code: 'LC' },
        { value: 'Saint Pierre and Miquelon', code: 'PM' },
        { value: 'Saint Vincent and the Grenadines', code: 'VC' },
        { value: 'Samoa', code: 'WS' },
        { value: 'San Marino', code: 'SM' },
        { value: 'Sao Tome and Principe', code: 'ST' },
        { value: 'Saudi Arabia', code: 'SA' },
        { value: 'Senegal', code: 'SN' },
        { value: 'Serbia and Montenegro', code: 'CS' },
        { value: 'Seychelles', code: 'SC' },
        { value: 'Sierra Leone', code: 'SL' },
        { value: 'Singapore', code: 'SG' },
        { value: 'Slovakia', code: 'SK' },
        { value: 'Slovenia', code: 'SI' },
        { value: 'Solomon Islands', code: 'SB' },
        { value: 'Somalia', code: 'SO' },
        { value: 'South Africa', code: 'ZA' },
        { value: 'South Georgia and the South Sandwich Islands', code: 'GS' },
        { value: 'Spain', code: 'ES' },
        { value: 'Sri Lanka', code: 'LK' },
        { value: 'Sudan', code: 'SD' },
        { value: 'Suriname', code: 'SR' },
        { value: 'Svalbard and Jan Mayen', code: 'SJ' },
        { value: 'Swaziland', code: 'SZ' },
        { value: 'Sweden', code: 'SE' },
        { value: 'Switzerland', code: 'CH' },
        { value: 'Syrian Arab Republic', code: 'SY' },
        { value: 'Taiwan', code: 'TW' },
        { value: 'Tajikistan', code: 'TJ' },
        { value: 'Tanzania, United Republic of', code: 'TZ' },
        { value: 'Thailand', code: 'TH' },
        { value: 'Timor-Leste', code: 'TL' },
        { value: 'Togo', code: 'TG' },
        { value: 'Tokelau', code: 'TK' },
        { value: 'Tonga', code: 'TO' },
        { value: 'Trinidad and Tobago', code: 'TT' },
        { value: 'Tunisia', code: 'TN' },
        { value: 'Turkey', code: 'TR' },
        { value: 'Turkmenistan', code: 'TM' },
        { value: 'Turks and Caicos Islands', code: 'TC' },
        { value: 'Tuvalu', code: 'TV' },
        { value: 'Uganda', code: 'UG' },
        { value: 'Ukraine', code: 'UA' },
        { value: 'United Arab Emirates', code: 'AE' },
        { value: 'United Kingdom', code: 'GB' },
        { value: 'United States', code: 'US' },
        { value: 'United States Minor Outlying Islands', code: 'UM' },
        { value: 'Uruguay', code: 'UY' },
        { value: 'Uzbekistan', code: 'UZ' },
        { value: 'Vanuatu', code: 'VU' },
        { value: 'Venezuela', code: 'VE' },
        { value: 'Viet Nam', code: 'VN' },
        { value: 'Virgin Islands, British', code: 'VG' },
        { value: 'Virgin Islands, U.S.', code: 'VI' },
        { value: 'Wallis and Futuna', code: 'WF' },
        { value: 'Western Sahara', code: 'EH' },
        { value: 'Yemen', code: 'YE' },
        { value: 'Zambia', code: 'ZM' },
        { value: 'Zimbabwe', code: 'ZW' }
    ],
    dropdown: {
        enabled: 1, // suggest tags after a single character input
        classname: 'extra-properties' // custom class for the suggestions dropdown
    } // map tags' values to this property name, so this property will be the actual value and not the printed value on the screen
})

tagify.on('click', function (e) {
    console.log(e.detail);
});

tagify.on('remove', function (e) {
    console.log(e.detail);
});

tagify.on('add', function (e) {
    console.log("original Input:", tagify.DOM.originalInput);
    console.log("original Input's value:", tagify.DOM.originalInput.value);
    console.log("event detail:", e.detail);
});

// add the first 2 tags and makes them readonly
var tagsToAdd = tagify.whitelist.slice(0, 2)
tagify.addTags(tagsToAdd)
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-readonly'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-readonly'>readonly</a></h2>
        </header>
        

        
                <p>
                    If the original input field has a <code>readonly</code> attribute, then, via CSS, Tagify blocks interactions with <code>pointer-events: none;</code>
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input readonly value=&#39;tag1, tag 2, another tag&#39;&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input[readonly]'),
    tagify = new Tagify(input);</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/RwaPWmR'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input readonly value='tag1, tag 2, another tag'>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input[readonly]'),
    tagify = new Tagify(input);
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-readonly-mixed'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-readonly-mixed'>readonly-mixed</a></h2>
        </header>
        

        
                <p>
                    Tags that are read-only mixed with removable tags
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input
    name=&#39;readonly-mix&#39;
    placeholder=&#39;Type something&#39;
    value=&#39;[
        {
            &quot;value&quot;: &quot;foo&quot;,
            &quot;readonly&quot;: true,
            &quot;title&quot;: &quot;read-only tag&quot;
        },
        {
            &quot;value&quot;: &quot;bar&quot;
        },
        {
            &quot;value&quot;: &quot;Locked tag&quot;,
            &quot;readonly&quot;: true,
            &quot;title&quot;: &quot;Another readonly tag&quot;
        }
    ]&#39;
/&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input[name=readonly-mix]'),
    tagify = new Tagify(input)</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/KKzpdLR'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input
    name='readonly-mix'
    placeholder='Type something'
    value='[
        {
            "value": "foo",
            "readonly": true,
            "title": "read-only tag"
        },
        {
            "value": "bar"
        },
        {
            "value": "Locked tag",
            "readonly": true,
            "title": "Another readonly tag"
        }
    ]'
/>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input[name=readonly-mix]'),
    tagify = new Tagify(input)
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-disabled'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-disabled'>disabled</a></h2>
        </header>
        

        
                <p>
                    If the original input field has a <code>disabled</code> attribute, then, via CSS, Tagify blocks interactions with <code>pointer-events: none;</code>
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input disabled value=&#39;tag1, tag 2, another tag&#39;&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input[disabled]'),
    tagify = new Tagify(input);</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/dyWYJbX'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input disabled value='tag1, tag 2, another tag'>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input[disabled]'),
    tagify = new Tagify(input);
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-drag-sort'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-drag-sort'>Drag &amp; Sort</a></h2>
        </header>
        

        
                <p>
                    To be able to sort tags by draging, a 3rd-party script is needed. In this example I will be using my own -
                    lightweight & simple native HTML5 <a href='https://www.npmjs.com/package/@yaireo/dragsort' target='_blank'><em>dragsort</em></a> script.
                </p>
                <p>
                    Any <em>drag & drop</em> script would do that is able to also sort the elements. The only requirement is that when
                    the sorting it done, <code>tagify.updateValueByDOMTags()</code> must be called to sync Tagify with the change.
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input name=&#39;drag-sort&#39; value=&#39;tag 1, tag 2, tag 3, tag 4, tag 5, tag 6&#39;/&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input[name=drag-sort]'),
    tagify = new Tagify(input);

// using 3-party script "dragsort"
// https://github.com/yairEO/dragsort
var dragsort = new DragSort(tagify.DOM.scope, {
    selector:'.' + tagify.settings.classNames.tag,
    callbacks: {
        dragEnd: onDragEnd
    }
})

function onDragEnd(elm){
    tagify.updateValueByDOMTags()
}</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/jOqYOVJ'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input name='drag-sort' value='tag 1, tag 2, tag 3, tag 4, tag 5, tag 6'/>
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input[name=drag-sort]'),
    tagify = new Tagify(input);

// using 3-party script "dragsort"
// https://github.com/yairEO/dragsort
var dragsort = new DragSort(tagify.DOM.scope, {
    selector:'.' + tagify.settings.classNames.tag,
    callbacks: {
        dragEnd: onDragEnd
    }
})

function onDragEnd(elm){
    tagify.updateValueByDOMTags()
}
        })()
    </script>
</section>


            <!----------- SECTION -------------->
            


<section id='section-mode-select'>
    <aside class='leftSide'>
        
        <header>
            <h2><a href='#section-mode-select'>single-value select</a></h2>
        </header>
        

        
                <p>
                    Similar to native <code>&lt;Select&gt;</code> element, but allows free text as value.
                </p>
                <p>
                    If the `enforceWhitelist` setting is set to `true` and a tag is currently selected,
                    user text input will be disabled, so altering the currently selected tag is forbidden.
                </p>
            

        <details>
            <summary>Code</summary>
            <h3>HTML</h3>
            <pre class="language-markup"><code>&lt;input name=&#39;mode-select&#39; placeholder=&quot;Please select&quot; /&gt;</code></pre>
            <h3>JAVASCRIPT</h3>
            <pre class='language-js'><code>var input = document.querySelector('input[name=mode-select]'),
    tagify = new Tagify(input, {
        enforceWhitelist: true,
        mode : "select",
        whitelist: ["first option", "second option", "third option"],
        blacklist: ['foo', 'bar'],
    })

// bind events
tagify.on('add', onAddTag)
tagify.DOM.input.addEventListener('focus', onSelectFocus)

function onAddTag(e){
    console.log(e.detail)
}

function onSelectFocus(e){
    console.log(e)
}</code></pre>
            
        </details>
    </aside>

    <aside class='rightSide'>
        <a class='demoLink' title='View in Codepen' target='_blank' href='https://codepen.io/vsync/pen/yLONYdx'>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
                <use class="logo-use" xlink:href="#codepen-logo"/>
            </svg>
        </a>
        <input name='mode-select' placeholder="Please select" />
    </aside>

    <script>
        (function(){
        var input = document.querySelector('input[name=mode-select]'),
    tagify = new Tagify(input, {
        enforceWhitelist: true,
        mode : "select",
        whitelist: ["first option", "second option", "third option"],
        blacklist: ['foo', 'bar'],
    })

// bind events
tagify.on('add', onAddTag)
tagify.DOM.input.addEventListener('focus', onSelectFocus)

function onAddTag(e){
    console.log(e.detail)
}

function onSelectFocus(e){
    console.log(e)
}
        })()
    </script>
</section>

        </form>

        <script>
    document.forms[0].reset();

    // get the sticky element
    const stickyElm = document.querySelector('header')

    const observer = new IntersectionObserver(
        ([e]) => e.target.classList.toggle('isSticky', e.intersectionRatio < 1),
        {threshold: [1]}
    );

    observer.observe(stickyElm)
</script>

<script>
    window.Prism = window.Prism || {};
    window.Prism.manual = true;
    setTimeout(function(){
        Prism.highlightAll();
    }, 500)
</script>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css">
<script src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js'></script>

<script src='https://unpkg.com/@yaireo/knobs'></script>
<script>
 (function(){
    var settings = {
        theme: {
            flow: "compact"
        },
        live: false,
        // 0 - starts as hidden, 1 - starts as visible, 2 - always visible
        visible: 1,
        CSSVarTarget: document.querySelectorAll('.tagify'),
        knobs: [
            {
                label: 'Show Original Input Element',
                type: 'checkbox',
                checked: false,
                onChange(e){
                    document.body.classList[e.target.checked ? 'add' : 'remove']('showOriginal')
                }
            },
            "Dynamic values",
            {
                cssVar: ['tag-pad', 'em'],
                label: 'tag-pad',
                type: 'range',
                value: .5, min: 0, max: 1.5, step: 0.01,
            },
            {
                cssVar: ['tag-border-radius', 'px'],
                label: 'tag-border-radius',
                type: 'range',
                value: 3, min: 0, max: 25
            },
            {
                cssVar: ['tag-inset-shadow-size', 'em'],
                label: 'tag-inset-shadow-size',
                type: 'range',
                min: 0, max: 2 , step:.1,
            },
            {
                cssVar: ['tag--min-width', 'ch'],
                label: 'tag--min-width',
                type: 'range',
                value: 1, min: 0, max: 20,
            },
            {
                cssVar: ['tag--max-width', 'ch'],
                label: 'tag--max-width',
                type: 'range',
                value: '100%', min: 1, max: 20,
            },
            {
                cssVar: ['loader-size', 'em'],
                label: 'loader-size',
                type: 'range',
                value: 'auto', min:.1, max: 1.4, step: 0.05
            },
            {
                cssVar: ['tagify-dd-item--hidden-duration', 'ms', document.documentElement],
                label: 'tagify-dd-item--hidden-duration',
                type: 'range',
                value: '300', min: 0, max: 3000, step: 50
            },
            {
                cssVar: ['tagify-dd-item-pad', 'em', document.documentElement],
                label: 'tagify-dd-item-pad',
                type: 'range',
                value: .3, min: 0, max: 1.5, step: 0.01,
            },

            "Colors",
            {
                cssVar: ['tag-bg'],
                label: 'tag-bg',
                type: 'color',
                value: '#E5E5E5',
            },
            {
                cssVar: ['tag-hover'],
                label: 'tag-hover',
                type: 'color',
                value: '#D3E2E2',
            },
            {
                cssVar: ['tags-border-color'],
                label: 'tags-border-color',
                type: 'color',
                value: '#E5E5E5',
            },
            {
                cssVar: ['tags-hover-border-color'],
                label: 'tags-hover-border-color',
                type: 'color',
                value: '#CCCCCC',
            },
            {
                cssVar: ['tags-focus-border-color'],
                label: 'tags-focus-border-color',
                type: 'color',
                value: '#85C8EA',
                onChange(e, knobData) {
                    // those should match:
                    document.documentElement.style.setProperty('--tagify-dd-color-primary', knobData.value)
                }
            },

            {
                cssVar: ['tagify-dd-text-color', null, document.documentElement],
                label: 'tagify-dd-text-color',
                type: 'color',
            },
            {
                cssVar: ['tagify-dd-bg-color', null, document.documentElement],
                label: 'tagify-dd-bg-color',
                type: 'color',
            },

            {
                cssVar: ['tag-text-color'],
                label: 'tag-text-color',
                type: 'color',
                value: '#000000',
            },
            {
                cssVar: ['tag-text-color--edit'],
                label: 'tag-text-color--edit',
                type: 'color',
                value: '#000000',
            },
            {
                cssVar: ['tag-remove-bg'],
                label: 'tag-remove-bg',
                type: 'color',
                value: '#D39494',
            },
            {
                cssVar: ['tag-remove-btn-bg'],
                label: 'tag-remove-btn-bg',
                type: 'color',
                value: 'none',
            },
            {
                cssVar: ['tag-remove-btn-bg--hover'],
                label: 'tag-remove-btn-bg--hover',
                type: 'color',
                value: '#C77777',
            },
            {
                cssVar: ['tag-invalid-color'],
                label: 'tag-invalid-color',
                type: 'color',
                value: '#D39494',
            },
            {
                cssVar: ['tag-invalid-bg'],
                label: 'tag-invalid-bg',
                type: 'color',
                value: '#D39494',
            },
            {
                cssVar: ['placeholder-color'],
                label: 'placeholder-color',
                type: 'color',
                value: 'rgba(0,0,0,.4)',
            },
            {
                cssVar: ['placeholder-color-focus'],
                label: 'placeholder-color-focus',
                type: 'color',
                value: 'rgba(0,0,0,.25)',
            },
            {
                cssVar: ['input-color'],
                label: 'input-color',
                type: 'color',
                value: '#000000',
            },
        ]
    }

    var knobs = new Knobs(settings)

    knobs.settings.live = true

    setTimeout(knobs.toggle.bind(knobs, false), 2000)
 })()
</script>
    </body>
</html>
